/*
 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package and.awt.geom;

import java.nio.ByteOrder;

import com.vividsolutions.jts.io.WKBReader;
import com.vividsolutions.jts.io.WKBWriter;

/**
 * The <code>AffineTransform</code> class represents a 2D affine transform that
 * performs a linear mapping from 2D coordinates to other 2D coordinates that
 * preserves the "straightness" and "parallelness" of lines. Affine
 * transformations can be constructed using sequences of translations, scales,
 * flips, rotations, and shears.
 * <p>
 * Such a coordinate transformation can be represented by a 3 row by 3 column
 * matrix with an implied last row of [ 0 0 1 ]. This matrix transforms source
 * coordinates {@code (x,y)} into destination coordinates {@code (x',y')} by
 * considering them to be a column vector and multiplying the coordinate vector
 * by the matrix according to the following process:
 * 
 * <pre>
 *      [ x']   [  m00  m01  m02  ] [ x ]   [ m00x + m01y + m02 ]
 *      [ y'] = [  m10  m11  m12  ] [ y ] = [ m10x + m11y + m12 ]
 *      [ 1 ]   [   0    0    1   ] [ 1 ]   [         1         ]
 * </pre>
 * 
 * <h3><a name="quadrantapproximation">Handling 90-Degree Rotations</a></h3>
 * <p>
 * In some variations of the <code>rotate</code> methods in the
 * <code>AffineTransform</code> class, a double-precision argument specifies the
 * angle of rotation in radians. These methods have special handling for
 * rotations of approximately 90 degrees (including multiples such as 180, 270,
 * and 360 degrees), so that the common case of quadrant rotation is handled
 * more efficiently. This special handling can cause angles very close to
 * multiples of 90 degrees to be treated as if they were exact multiples of 90
 * degrees. For small multiples of 90 degrees the range of angles treated as a
 * quadrant rotation is approximately 0.00000121 degrees wide. This section
 * explains why such special care is needed and how it is implemented.
 * <p>
 * Since 90 degrees is represented as <code>PI/2</code> in radians, and since PI
 * is a transcendental (and therefore irrational) number, it is not possible to
 * exactly represent a multiple of 90 degrees as an exact double precision value
 * measured in radians. As a result it is theoretically impossible to describe
 * quadrant rotations (90, 180, 270 or 360 degrees) using these values. Double
 * precision floating point values can get very close to non-zero multiples of
 * <code>PI/2</code> but never close enough for the sine or cosine to be exactly
 * 0.0, 1.0 or -1.0. The implementations of <code>Math.sin()</code> and
 * <code>Math.cos()</code> correspondingly never return 0.0 for any case other
 * than <code>Math.sin(0.0)</code>. These same implementations do, however,
 * return exactly 1.0 and -1.0 for some range of numbers around each multiple of
 * 90 degrees since the correct answer is so close to 1.0 or -1.0 that the
 * double precision significand cannot represent the difference as accurately as
 * it can for numbers that are near 0.0.
 * <p>
 * The net result of these issues is that if the <code>Math.sin()</code> and
 * <code>Math.cos()</code> methods are used to directly generate the values for
 * the matrix modifications during these radian-based rotation operations then
 * the resulting transform is never strictly classifiable as a quadrant rotation
 * even for a simple case like <code>rotate(Math.PI/2.0)</code>, due to minor
 * variations in the matrix caused by the non-0.0 values obtained for the sine
 * and cosine. If these transforms are not classified as quadrant rotations then
 * subsequent code which attempts to optimize further operations based upon the
 * type of the transform will be relegated to its most general implementation.
 * <p>
 * Because quadrant rotations are fairly common, this class should handle these
 * cases reasonably quickly, both in applying the rotations to the transform and
 * in applying the resulting transform to the coordinates. To facilitate this
 * optimal handling, the methods which take an angle of rotation measured in
 * radians attempt to detect angles that are intended to be quadrant rotations
 * and treat them as such. These methods therefore treat an angle <em>theta</em>
 * as a quadrant rotation if either <code>Math.sin(<em>theta</em>)</code> or
 * <code>Math.cos(<em>theta</em>)</code> returns exactly 1.0 or -1.0. As a rule
 * of thumb, this property holds true for a range of approximately 0.0000000211
 * radians (or 0.00000121 degrees) around small multiples of
 * <code>Math.PI/2.0</code>.
 * 
 * @author Jim Graham
 * @since 1.2
 */
public class CopyOfAffineTransform implements Cloneable, java.io.Serializable {

	/*
	 * This constant is only useful for the cached type field. It indicates that
	 * the type has been decached and must be recalculated.
	 */
	private static final int TYPE_UNKNOWN = -1;

	/**
	 * This constant indicates that the transform defined by this object is an
	 * identity transform. An identity transform is one in which the output
	 * coordinates are always the same as the input coordinates. If this
	 * transform is anything other than the identity transform, the type will
	 * either be the constant GENERAL_TRANSFORM or a combination of the
	 * appropriate flag bits for the various coordinate conversions that this
	 * transform performs.
	 * 
	 * @see #TYPE_TRANSLATION
	 * @see #TYPE_UNIFORM_SCALE
	 * @see #TYPE_GENERAL_SCALE
	 * @see #TYPE_FLIP
	 * @see #TYPE_QUADRANT_ROTATION
	 * @see #TYPE_GENERAL_ROTATION
	 * @see #TYPE_GENERAL_TRANSFORM
	 * @see #getType
	 * @since 1.2
	 */
	public static final int TYPE_IDENTITY = 0;

	/**
	 * This flag bit indicates that the transform defined by this object
	 * performs a translation in addition to the conversions indicated by other
	 * flag bits. A translation moves the coordinates by a constant amount in x
	 * and y without changing the length or angle of vectors.
	 * 
	 * @see #TYPE_IDENTITY
	 * @see #TYPE_UNIFORM_SCALE
	 * @see #TYPE_GENERAL_SCALE
	 * @see #TYPE_FLIP
	 * @see #TYPE_QUADRANT_ROTATION
	 * @see #TYPE_GENERAL_ROTATION
	 * @see #TYPE_GENERAL_TRANSFORM
	 * @see #getType
	 * @since 1.2
	 */
	public static final int TYPE_TRANSLATION = 1;

	/**
	 * This flag bit indicates that the transform defined by this object
	 * performs a uniform scale in addition to the conversions indicated by
	 * other flag bits. A uniform scale multiplies the length of vectors by the
	 * same amount in both the x and y directions without changing the angle
	 * between vectors. This flag bit is mutually exclusive with the
	 * TYPE_GENERAL_SCALE flag.
	 * 
	 * @see #TYPE_IDENTITY
	 * @see #TYPE_TRANSLATION
	 * @see #TYPE_GENERAL_SCALE
	 * @see #TYPE_FLIP
	 * @see #TYPE_QUADRANT_ROTATION
	 * @see #TYPE_GENERAL_ROTATION
	 * @see #TYPE_GENERAL_TRANSFORM
	 * @see #getType
	 * @since 1.2
	 */
	public static final int TYPE_UNIFORM_SCALE = 2;

	/**
	 * This flag bit indicates that the transform defined by this object
	 * performs a general scale in addition to the conversions indicated by
	 * other flag bits. A general scale multiplies the length of vectors by
	 * different amounts in the x and y directions without changing the angle
	 * between perpendicular vectors. This flag bit is mutually exclusive with
	 * the TYPE_UNIFORM_SCALE flag.
	 * 
	 * @see #TYPE_IDENTITY
	 * @see #TYPE_TRANSLATION
	 * @see #TYPE_UNIFORM_SCALE
	 * @see #TYPE_FLIP
	 * @see #TYPE_QUADRANT_ROTATION
	 * @see #TYPE_GENERAL_ROTATION
	 * @see #TYPE_GENERAL_TRANSFORM
	 * @see #getType
	 * @since 1.2
	 */
	public static final int TYPE_GENERAL_SCALE = 4;

	/**
	 * This constant is a bit mask for any of the scale flag bits.
	 * 
	 * @see #TYPE_UNIFORM_SCALE
	 * @see #TYPE_GENERAL_SCALE
	 * @since 1.2
	 */
	public static final int TYPE_MASK_SCALE = (TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE);

	/**
	 * This flag bit indicates that the transform defined by this object
	 * performs a mirror image flip about some axis which changes the normally
	 * right handed coordinate system into a left handed system in addition to
	 * the conversions indicated by other flag bits. A right handed coordinate
	 * system is one where the positive X axis rotates counterclockwise to
	 * overlay the positive Y axis similar to the direction that the fingers on
	 * your right hand curl when you stare end on at your thumb. A left handed
	 * coordinate system is one where the positive X axis rotates clockwise to
	 * overlay the positive Y axis similar to the direction that the fingers on
	 * your left hand curl. There is no mathematical way to determine the angle
	 * of the original flipping or mirroring transformation since all angles of
	 * flip are identical given an appropriate adjusting rotation.
	 * 
	 * @see #TYPE_IDENTITY
	 * @see #TYPE_TRANSLATION
	 * @see #TYPE_UNIFORM_SCALE
	 * @see #TYPE_GENERAL_SCALE
	 * @see #TYPE_QUADRANT_ROTATION
	 * @see #TYPE_GENERAL_ROTATION
	 * @see #TYPE_GENERAL_TRANSFORM
	 * @see #getType
	 * @since 1.2
	 */
	public static final int TYPE_FLIP = 64;
	/*
	 * NOTE: TYPE_FLIP was added after GENERAL_TRANSFORM was in public
	 * circulation and the flag bits could no longer be conveniently renumbered
	 * without introducing binary incompatibility in outside code.
	 */

	/**
	 * This flag bit indicates that the transform defined by this object
	 * performs a quadrant rotation by some multiple of 90 degrees in addition
	 * to the conversions indicated by other flag bits. A rotation changes the
	 * angles of vectors by the same amount regardless of the original direction
	 * of the vector and without changing the length of the vector. This flag
	 * bit is mutually exclusive with the TYPE_GENERAL_ROTATION flag.
	 * 
	 * @see #TYPE_IDENTITY
	 * @see #TYPE_TRANSLATION
	 * @see #TYPE_UNIFORM_SCALE
	 * @see #TYPE_GENERAL_SCALE
	 * @see #TYPE_FLIP
	 * @see #TYPE_GENERAL_ROTATION
	 * @see #TYPE_GENERAL_TRANSFORM
	 * @see #getType
	 * @since 1.2
	 */
	public static final int TYPE_QUADRANT_ROTATION = 8;

	/**
	 * This flag bit indicates that the transform defined by this object
	 * performs a rotation by an arbitrary angle in addition to the conversions
	 * indicated by other flag bits. A rotation changes the angles of vectors by
	 * the same amount regardless of the original direction of the vector and
	 * without changing the length of the vector. This flag bit is mutually
	 * exclusive with the TYPE_QUADRANT_ROTATION flag.
	 * 
	 * @see #TYPE_IDENTITY
	 * @see #TYPE_TRANSLATION
	 * @see #TYPE_UNIFORM_SCALE
	 * @see #TYPE_GENERAL_SCALE
	 * @see #TYPE_FLIP
	 * @see #TYPE_QUADRANT_ROTATION
	 * @see #TYPE_GENERAL_TRANSFORM
	 * @see #getType
	 * @since 1.2
	 */
	public static final int TYPE_GENERAL_ROTATION = 16;

	/**
	 * This constant is a bit mask for any of the rotation flag bits.
	 * 
	 * @see #TYPE_QUADRANT_ROTATION
	 * @see #TYPE_GENERAL_ROTATION
	 * @since 1.2
	 */
	public static final int TYPE_MASK_ROTATION = (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION);

	/**
	 * This constant indicates that the transform defined by this object
	 * performs an arbitrary conversion of the input coordinates. If this
	 * transform can be classified by any of the above constants, the type will
	 * either be the constant TYPE_IDENTITY or a combination of the appropriate
	 * flag bits for the various coordinate conversions that this transform
	 * performs.
	 * 
	 * @see #TYPE_IDENTITY
	 * @see #TYPE_TRANSLATION
	 * @see #TYPE_UNIFORM_SCALE
	 * @see #TYPE_GENERAL_SCALE
	 * @see #TYPE_FLIP
	 * @see #TYPE_QUADRANT_ROTATION
	 * @see #TYPE_GENERAL_ROTATION
	 * @see #getType
	 * @since 1.2
	 */
	public static final int TYPE_GENERAL_TRANSFORM = 32;

	/**
	 * This constant is used for the internal state variable to indicate that no
	 * calculations need to be performed and that the source coordinates only
	 * need to be copied to their destinations to complete the transformation
	 * equation of this transform.
	 * 
	 * @see #APPLY_TRANSLATE
	 * @see #APPLY_SCALE
	 * @see #APPLY_SHEAR
	 * @see #state
	 */
	static final int APPLY_IDENTITY = 0;

	/**
	 * This constant is used for the internal state variable to indicate that
	 * the translation components of the matrix (m02 and m12) need to be added
	 * to complete the transformation equation of this transform.
	 * 
	 * @see #APPLY_IDENTITY
	 * @see #APPLY_SCALE
	 * @see #APPLY_SHEAR
	 * @see #state
	 */
	static final int APPLY_TRANSLATE = 1;

	/**
	 * This constant is used for the internal state variable to indicate that
	 * the scaling components of the matrix (m00 and m11) need to be factored in
	 * to complete the transformation equation of this transform. If the
	 * APPLY_SHEAR bit is also set then it indicates that the scaling components
	 * are not both 0.0. If the APPLY_SHEAR bit is not also set then it
	 * indicates that the scaling components are not both 1.0. If neither the
	 * APPLY_SHEAR nor the APPLY_SCALE bits are set then the scaling components
	 * are both 1.0, which means that the x and y components contribute to the
	 * transformed coordinate, but they are not multiplied by any scaling
	 * factor.
	 * 
	 * @see #APPLY_IDENTITY
	 * @see #APPLY_TRANSLATE
	 * @see #APPLY_SHEAR
	 * @see #state
	 */
	static final int APPLY_SCALE = 2;

	/**
	 * This constant is used for the internal state variable to indicate that
	 * the shearing components of the matrix (m01 and m10) need to be factored
	 * in to complete the transformation equation of this transform. The
	 * presence of this bit in the state variable changes the interpretation of
	 * the APPLY_SCALE bit as indicated in its documentation.
	 * 
	 * @see #APPLY_IDENTITY
	 * @see #APPLY_TRANSLATE
	 * @see #APPLY_SCALE
	 * @see #state
	 */
	static final int APPLY_SHEAR = 4;

	/*
	 * For methods which combine together the state of two separate transforms
	 * and dispatch based upon the combination, these constants specify how far
	 * to shift one of the states so that the two states are mutually
	 * non-interfering and provide constants for testing the bits of the shifted
	 * (HI) state. The methods in this class use the convention that the state
	 * of "this" transform is unshifted and the state of the "other" or
	 * "argument" transform is shifted (HI).
	 */
	private static final int HI_SHIFT = 3;
	private static final int HI_IDENTITY = APPLY_IDENTITY << HI_SHIFT;
	private static final int HI_TRANSLATE = APPLY_TRANSLATE << HI_SHIFT;
	private static final int HI_SCALE = APPLY_SCALE << HI_SHIFT;
	private static final int HI_SHEAR = APPLY_SHEAR << HI_SHIFT;

	/**
	 * The X coordinate scaling element of the 3x3 affine transformation matrix.
	 * 
	 * @serial
	 */
	double A11;

	/**
	 * The Y coordinate shearing element of the 3x3 affine transformation
	 * matrix.
	 * 
	 * @serial
	 */
	double A21;

	/**
	 * The X coordinate shearing element of the 3x3 affine transformation
	 * matrix.
	 * 
	 * @serial
	 */
	double A12;

	/**
	 * The Y coordinate scaling element of the 3x3 affine transformation matrix.
	 * 
	 * @serial
	 */
	double A22;

	/**
	 * The X coordinate of the translation element of the 3x3 affine
	 * transformation matrix.
	 * 
	 * @serial
	 */
	double A13;

	/**
	 * The Y coordinate of the translation element of the 3x3 affine
	 * transformation matrix.
	 * 
	 * @serial
	 */
	double A23;

	/**
	 * This field keeps track of which components of the matrix need to be
	 * applied when performing a transformation.
	 * 
	 * @see #APPLY_IDENTITY
	 * @see #APPLY_TRANSLATE
	 * @see #APPLY_SCALE
	 * @see #APPLY_SHEAR
	 */
	transient int state;

	/**
	 * This field caches the current transformation type of the matrix.
	 * 
	 * @see #TYPE_IDENTITY
	 * @see #TYPE_TRANSLATION
	 * @see #TYPE_UNIFORM_SCALE
	 * @see #TYPE_GENERAL_SCALE
	 * @see #TYPE_FLIP
	 * @see #TYPE_QUADRANT_ROTATION
	 * @see #TYPE_GENERAL_ROTATION
	 * @see #TYPE_GENERAL_TRANSFORM
	 * @see #TYPE_UNKNOWN
	 * @see #getType
	 */
	private transient int type;

	private CopyOfAffineTransform(double A11, double A21, double A12, double A22,
			double A13, double A23, int state) {
		this.A11 = A11;
		this.A21 = A21;
		this.A12 = A12;
		this.A22 = A22;
		this.A13 = A13;
		this.A23 = A23;
		this.state = state;
		this.type = TYPE_UNKNOWN;
	}

	/**
	 * Constructs a new <code>AffineTransform</code> representing the Identity
	 * transformation.
	 * 
	 * @since 1.2
	 */
	public CopyOfAffineTransform() {
		A11 = A22 = 1.0;
		// m01 = m10 = m02 = m12 = 0.0; /* Not needed. */
		// state = APPLY_IDENTITY; /* Not needed. */
		// type = TYPE_IDENTITY; /* Not needed. */
	}

	/**
	 * Constructs a new <code>AffineTransform</code> that is a copy of the
	 * specified <code>AffineTransform</code> object.
	 * 
	 * @param Tx
	 *            the <code>AffineTransform</code> object to copy
	 * @since 1.2
	 */
	public CopyOfAffineTransform(CopyOfAffineTransform Tx) {
		this.A11 = Tx.A11;
		this.A21 = Tx.A21;
		this.A12 = Tx.A12;
		this.A22 = Tx.A22;
		this.A13 = Tx.A13;
		this.A23 = Tx.A23;
		this.state = Tx.state;
		this.type = Tx.type;
	}

	/**
	 * Constructs a new <code>AffineTransform</code> from 6 floating point
	 * values representing the 6 specifiable entries of the 3x3 transformation
	 * matrix.
	 * 
	 * @param m00
	 *            the X coordinate scaling element of the 3x3 matrix
	 * @param m10
	 *            the Y coordinate shearing element of the 3x3 matrix
	 * @param m01
	 *            the X coordinate shearing element of the 3x3 matrix
	 * @param m11
	 *            the Y coordinate scaling element of the 3x3 matrix
	 * @param m02
	 *            the X coordinate translation element of the 3x3 matrix
	 * @param m12
	 *            the Y coordinate translation element of the 3x3 matrix
	 * @since 1.2
	 */

	public CopyOfAffineTransform(float m00, float m10, float m01, float m11,
			float m02, float m12) {
		this.A11 = m00;
		this.A21 = m10;
		this.A12 = m01;
		this.A22 = m11;
		this.A13 = m02;
		this.A23 = m12;
		updateState();
	}

	/**
	 * Constructs a new <code>AffineTransform</code> from an array of floating
	 * point values representing either the 4 non-translation entries or the 6
	 * specifiable entries of the 3x3 transformation matrix. The values are
	 * retrieved from the array as
	 * {&nbsp;m00&nbsp;m10&nbsp;m01&nbsp;m11&nbsp;[m02&nbsp;m12]}.
	 * 
	 * @param flatmatrix
	 *            the float array containing the values to be set in the new
	 *            <code>AffineTransform</code> object. The length of the array
	 *            is assumed to be at least 4. If the length of the array is
	 *            less than 6, only the first 4 values are taken. If the length
	 *            of the array is greater than 6, the first 6 values are taken.
	 * @since 1.2
	 */
	public CopyOfAffineTransform(float[] flatmatrix) {
		A11 = flatmatrix[0];
		A21 = flatmatrix[1];
		A12 = flatmatrix[2];
		A22 = flatmatrix[3];
		if (flatmatrix.length > 5) {
			A13 = flatmatrix[4];
			A23 = flatmatrix[5];
		}
		updateState();
	}

	/**
	 * Constructs a new <code>AffineTransform</code> from 6 double precision
	 * values representing the 6 specifiable entries of the 3x3 transformation
	 * matrix.
	 * 
	 * @param m00
	 *            the X coordinate scaling element of the 3x3 matrix
	 * @param m10
	 *            the Y coordinate shearing element of the 3x3 matrix
	 * @param m01
	 *            the X coordinate shearing element of the 3x3 matrix
	 * @param m11
	 *            the Y coordinate scaling element of the 3x3 matrix
	 * @param m02
	 *            the X coordinate translation element of the 3x3 matrix
	 * @param m12
	 *            the Y coordinate translation element of the 3x3 matrix
	 * @since 1.2
	 */
	public CopyOfAffineTransform(double m00, double m10, double m01, double m11,
			double m02, double m12) {
		this.A11 = m00;
		this.A21 = m10;
		this.A12 = m01;
		this.A22 = m11;
		this.A13 = m02;
		this.A23 = m12;
		updateState();
	}

	/**
	 * Constructs a new <code>AffineTransform</code> from an array of double
	 * precision values representing either the 4 non-translation entries or the
	 * 6 specifiable entries of the 3x3 transformation matrix. The values are
	 * retrieved from the array as
	 * {&nbsp;m00&nbsp;m10&nbsp;m01&nbsp;m11&nbsp;[m02&nbsp;m12]}.
	 * 
	 * @param flatmatrix
	 *            the double array containing the values to be set in the new
	 *            <code>AffineTransform</code> object. The length of the array
	 *            is assumed to be at least 4. If the length of the array is
	 *            less than 6, only the first 4 values are taken. If the length
	 *            of the array is greater than 6, the first 6 values are taken.
	 * @since 1.2
	 */
	public CopyOfAffineTransform(double[] flatmatrix) {
		A11 = flatmatrix[0];
		A21 = flatmatrix[1];
		A12 = flatmatrix[2];
		A22 = flatmatrix[3];
		if (flatmatrix.length > 5) {
			A13 = flatmatrix[4];
			A23 = flatmatrix[5];
		}
		updateState();
	}

	/**
	 * Returns a transform representing a translation transformation. The matrix
	 * representing the returned transform is:
	 * 
	 * <pre>
	 *          [   1    0    tx  ]
	 *          [   0    1    ty  ]
	 *          [   0    0    1   ]
	 * </pre>
	 * 
	 * @param tx
	 *            the distance by which coordinates are translated in the X axis
	 *            direction
	 * @param ty
	 *            the distance by which coordinates are translated in the Y axis
	 *            direction
	 * @return an <code>AffineTransform</code> object that represents a
	 *         translation transformation, created with the specified vector.
	 * @since 1.2
	 */
	public static CopyOfAffineTransform getTranslateInstance(double tx, double ty) {
		CopyOfAffineTransform Tx = new CopyOfAffineTransform();
		Tx.setToTranslation(tx, ty);
		return Tx;
	}

	/**
	 * Returns a transform representing a rotation transformation. The matrix
	 * representing the returned transform is:
	 * 
	 * <pre>
	 *          [   cos(theta)    -sin(theta)    0   ]
	 *          [   sin(theta)     cos(theta)    0   ]
	 *          [       0              0         1   ]
	 * </pre>
	 * 
	 * Rotating by a positive angle theta rotates points on the positive X axis
	 * toward the positive Y axis. Note also the discussion of <a
	 * href="#quadrantapproximation">Handling 90-Degree Rotations</a> above.
	 * 
	 * @param theta
	 *            the angle of rotation measured in radians
	 * @return an <code>AffineTransform</code> object that is a rotation
	 *         transformation, created with the specified angle of rotation.
	 * @since 1.2
	 */
	public static CopyOfAffineTransform getRotateInstance(double theta) {
		CopyOfAffineTransform Tx = new CopyOfAffineTransform();
		Tx.setToRotation(theta);
		return Tx;
	}

	/**
	 * Returns a transform that rotates coordinates around an anchor point. This
	 * operation is equivalent to translating the coordinates so that the anchor
	 * point is at the origin (S1), then rotating them about the new origin
	 * (S2), and finally translating so that the intermediate origin is restored
	 * to the coordinates of the original anchor point (S3).
	 * <p>
	 * This operation is equivalent to the following sequence of calls:
	 * 
	 * <pre>
	 * AffineTransform Tx = new AffineTransform();
	 * Tx.translate(anchorx, anchory); // S3: final translation
	 * Tx.rotate(theta); // S2: rotate around anchor
	 * Tx.translate(-anchorx, -anchory); // S1: translate anchor to origin
	 * </pre>
	 * 
	 * The matrix representing the returned transform is:
	 * 
	 * <pre>
	 *          [   cos(theta)    -sin(theta)    x-x*cos+y*sin  ]
	 *          [   sin(theta)     cos(theta)    y-x*sin-y*cos  ]
	 *          [       0              0               1        ]
	 * </pre>
	 * 
	 * Rotating by a positive angle theta rotates points on the positive X axis
	 * toward the positive Y axis. Note also the discussion of <a
	 * href="#quadrantapproximation">Handling 90-Degree Rotations</a> above.
	 * 
	 * @param theta
	 *            the angle of rotation measured in radians
	 * @param anchorx
	 *            the X coordinate of the rotation anchor point
	 * @param anchory
	 *            the Y coordinate of the rotation anchor point
	 * @return an <code>AffineTransform</code> object that rotates coordinates
	 *         around the specified point by the specified angle of rotation.
	 * @since 1.2
	 */
	public static CopyOfAffineTransform getRotateInstance(double theta,
			double anchorx, double anchory) {
		CopyOfAffineTransform Tx = new CopyOfAffineTransform();
		Tx.setToRotation(theta, anchorx, anchory);
		return Tx;
	}

	/**
	 * Returns a transform that rotates coordinates according to a rotation
	 * vector. All coordinates rotate about the origin by the same amount. The
	 * amount of rotation is such that coordinates along the former positive X
	 * axis will subsequently align with the vector pointing from the origin to
	 * the specified vector coordinates. If both <code>vecx</code> and
	 * <code>vecy</code> are 0.0, an identity transform is returned. This
	 * operation is equivalent to calling:
	 * 
	 * <pre>
	 * AffineTransform.getRotateInstance(Math.atan2(vecy, vecx));
	 * </pre>
	 * 
	 * @param vecx
	 *            the X coordinate of the rotation vector
	 * @param vecy
	 *            the Y coordinate of the rotation vector
	 * @return an <code>AffineTransform</code> object that rotates coordinates
	 *         according to the specified rotation vector.
	 * @since 1.6
	 */
	public static CopyOfAffineTransform getRotateInstance(double vecx, double vecy) {
		CopyOfAffineTransform Tx = new CopyOfAffineTransform();
		Tx.setToRotation(vecx, vecy);
		return Tx;
	}

	/**
	 * Returns a transform that rotates coordinates around an anchor point
	 * according to a rotation vector. All coordinates rotate about the
	 * specified anchor coordinates by the same amount. The amount of rotation
	 * is such that coordinates along the former positive X axis will
	 * subsequently align with the vector pointing from the origin to the
	 * specified vector coordinates. If both <code>vecx</code> and
	 * <code>vecy</code> are 0.0, an identity transform is returned. This
	 * operation is equivalent to calling:
	 * 
	 * <pre>
	 * AffineTransform.getRotateInstance(Math.atan2(vecy, vecx), anchorx, anchory);
	 * </pre>
	 * 
	 * @param vecx
	 *            the X coordinate of the rotation vector
	 * @param vecy
	 *            the Y coordinate of the rotation vector
	 * @param anchorx
	 *            the X coordinate of the rotation anchor point
	 * @param anchory
	 *            the Y coordinate of the rotation anchor point
	 * @return an <code>AffineTransform</code> object that rotates coordinates
	 *         around the specified point according to the specified rotation
	 *         vector.
	 * @since 1.6
	 */
	public static CopyOfAffineTransform getRotateInstance(double vecx, double vecy,
			double anchorx, double anchory) {
		CopyOfAffineTransform Tx = new CopyOfAffineTransform();
		Tx.setToRotation(vecx, vecy, anchorx, anchory);
		return Tx;
	}

	/**
	 * Returns a transform that rotates coordinates by the specified number of
	 * quadrants. This operation is equivalent to calling:
	 * 
	 * <pre>
	 * AffineTransform.getRotateInstance(numquadrants * Math.PI / 2.0);
	 * </pre>
	 * 
	 * Rotating by a positive number of quadrants rotates points on the positive
	 * X axis toward the positive Y axis.
	 * 
	 * @param numquadrants
	 *            the number of 90 degree arcs to rotate by
	 * @return an <code>AffineTransform</code> object that rotates coordinates
	 *         by the specified number of quadrants.
	 * @since 1.6
	 */
	public static CopyOfAffineTransform getQuadrantRotateInstance(int numquadrants) {
		CopyOfAffineTransform Tx = new CopyOfAffineTransform();
		Tx.setToQuadrantRotation(numquadrants);
		return Tx;
	}

	/**
	 * Returns a transform that rotates coordinates by the specified number of
	 * quadrants around the specified anchor point. This operation is equivalent
	 * to calling:
	 * 
	 * <pre>
	 * AffineTransform.getRotateInstance(numquadrants * Math.PI / 2.0, anchorx,
	 * 		anchory);
	 * </pre>
	 * 
	 * Rotating by a positive number of quadrants rotates points on the positive
	 * X axis toward the positive Y axis.
	 * 
	 * @param numquadrants
	 *            the number of 90 degree arcs to rotate by
	 * @param anchorx
	 *            the X coordinate of the rotation anchor point
	 * @param anchory
	 *            the Y coordinate of the rotation anchor point
	 * @return an <code>AffineTransform</code> object that rotates coordinates
	 *         by the specified number of quadrants around the specified anchor
	 *         point.
	 * @since 1.6
	 */
	public static CopyOfAffineTransform getQuadrantRotateInstance(int numquadrants,
			double anchorx, double anchory) {
		CopyOfAffineTransform Tx = new CopyOfAffineTransform();
		Tx.setToQuadrantRotation(numquadrants, anchorx, anchory);
		return Tx;
	}

	/**
	 * Returns a transform representing a scaling transformation. The matrix
	 * representing the returned transform is:
	 * 
	 * <pre>
	 *          [   sx   0    0   ]
	 *          [   0    sy   0   ]
	 *          [   0    0    1   ]
	 * </pre>
	 * 
	 * @param sx
	 *            the factor by which coordinates are scaled along the X axis
	 *            direction
	 * @param sy
	 *            the factor by which coordinates are scaled along the Y axis
	 *            direction
	 * @return an <code>AffineTransform</code> object that scales coordinates by
	 *         the specified factors.
	 * @since 1.2
	 */
	public static CopyOfAffineTransform getScaleInstance(double sx, double sy) {
		CopyOfAffineTransform Tx = new CopyOfAffineTransform();
		Tx.setToScale(sx, sy);
		return Tx;
	}

	/**
	 * Returns a transform representing a shearing transformation. The matrix
	 * representing the returned transform is:
	 * 
	 * <pre>
	 *          [   1   shx   0   ]
	 *          [  shy   1    0   ]
	 *          [   0    0    1   ]
	 * </pre>
	 * 
	 * @param shx
	 *            the multiplier by which coordinates are shifted in the
	 *            direction of the positive X axis as a factor of their Y
	 *            coordinate
	 * @param shy
	 *            the multiplier by which coordinates are shifted in the
	 *            direction of the positive Y axis as a factor of their X
	 *            coordinate
	 * @return an <code>AffineTransform</code> object that shears coordinates by
	 *         the specified multipliers.
	 * @since 1.2
	 */
	public static CopyOfAffineTransform getShearInstance(double shx, double shy) {
		CopyOfAffineTransform Tx = new CopyOfAffineTransform();
		Tx.setToShear(shx, shy);
		return Tx;
	}

	/**
	 * Retrieves the flag bits describing the conversion properties of this
	 * transform. The return value is either one of the constants TYPE_IDENTITY
	 * or TYPE_GENERAL_TRANSFORM, or a combination of the appropriate flag bits.
	 * A valid combination of flag bits is an exclusive OR operation that can
	 * combine the TYPE_TRANSLATION flag bit in addition to either of the
	 * TYPE_UNIFORM_SCALE or TYPE_GENERAL_SCALE flag bits as well as either of
	 * the TYPE_QUADRANT_ROTATION or TYPE_GENERAL_ROTATION flag bits.
	 * 
	 * @return the OR combination of any of the indicated flags that apply to
	 *         this transform
	 * @see #TYPE_IDENTITY
	 * @see #TYPE_TRANSLATION
	 * @see #TYPE_UNIFORM_SCALE
	 * @see #TYPE_GENERAL_SCALE
	 * @see #TYPE_QUADRANT_ROTATION
	 * @see #TYPE_GENERAL_ROTATION
	 * @see #TYPE_GENERAL_TRANSFORM
	 * @since 1.2
	 */
	public int getType() {
		if (type == TYPE_UNKNOWN) {
			calculateType();
		}
		return type;
	}

	/**
	 * This is the utility function to calculate the flag bits when they have
	 * not been cached.
	 * 
	 * @see #getType
	 */
	@SuppressWarnings("fallthrough")
	private void calculateType() {
		int ret = TYPE_IDENTITY;
		boolean sgn0, sgn1;
		double M0, M1, M2, M3;
		updateState();
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			ret = TYPE_TRANSLATION;
			/* NOBREAK */
		case (APPLY_SHEAR | APPLY_SCALE):
			if ((M0 = A11) * (M2 = A12) + (M3 = A21) * (M1 = A22) != 0) {
				// Transformed unit vectors are not perpendicular...
				this.type = TYPE_GENERAL_TRANSFORM;
				return;
			}
			sgn0 = (M0 >= 0.0);
			sgn1 = (M1 >= 0.0);
			if (sgn0 == sgn1) {
				// sgn(M0) == sgn(M1) therefore sgn(M2) == -sgn(M3)
				// This is the "unflipped" (right-handed) state
				if (M0 != M1 || M2 != -M3) {
					ret |= (TYPE_GENERAL_ROTATION | TYPE_GENERAL_SCALE);
				} else if (M0 * M1 - M2 * M3 != 1.0) {
					ret |= (TYPE_GENERAL_ROTATION | TYPE_UNIFORM_SCALE);
				} else {
					ret |= TYPE_GENERAL_ROTATION;
				}
			} else {
				// sgn(M0) == -sgn(M1) therefore sgn(M2) == sgn(M3)
				// This is the "flipped" (left-handed) state
				if (M0 != -M1 || M2 != M3) {
					ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP | TYPE_GENERAL_SCALE);
				} else if (M0 * M1 - M2 * M3 != 1.0) {
					ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP | TYPE_UNIFORM_SCALE);
				} else {
					ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP);
				}
			}
			break;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
			ret = TYPE_TRANSLATION;
			/* NOBREAK */
		case (APPLY_SHEAR):
			sgn0 = ((M0 = A12) >= 0.0);
			sgn1 = ((M1 = A21) >= 0.0);
			if (sgn0 != sgn1) {
				// Different signs - simple 90 degree rotation
				if (M0 != -M1) {
					ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE);
				} else if (M0 != 1.0 && M0 != -1.0) {
					ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE);
				} else {
					ret |= TYPE_QUADRANT_ROTATION;
				}
			} else {
				// Same signs - 90 degree rotation plus an axis flip too
				if (M0 == M1) {
					ret |= (TYPE_QUADRANT_ROTATION | TYPE_FLIP | TYPE_UNIFORM_SCALE);
				} else {
					ret |= (TYPE_QUADRANT_ROTATION | TYPE_FLIP | TYPE_GENERAL_SCALE);
				}
			}
			break;
		case (APPLY_SCALE | APPLY_TRANSLATE):
			ret = TYPE_TRANSLATION;
			/* NOBREAK */
		case (APPLY_SCALE):
			sgn0 = ((M0 = A11) >= 0.0);
			sgn1 = ((M1 = A22) >= 0.0);
			if (sgn0 == sgn1) {
				if (sgn0) {
					// Both scaling factors non-negative - simple scale
					// Note: APPLY_SCALE implies M0, M1 are not both 1
					if (M0 == M1) {
						ret |= TYPE_UNIFORM_SCALE;
					} else {
						ret |= TYPE_GENERAL_SCALE;
					}
				} else {
					// Both scaling factors negative - 180 degree rotation
					if (M0 != M1) {
						ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE);
					} else if (M0 != -1.0) {
						ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE);
					} else {
						ret |= TYPE_QUADRANT_ROTATION;
					}
				}
			} else {
				// Scaling factor signs different - flip about some axis
				if (M0 == -M1) {
					if (M0 == 1.0 || M0 == -1.0) {
						ret |= TYPE_FLIP;
					} else {
						ret |= (TYPE_FLIP | TYPE_UNIFORM_SCALE);
					}
				} else {
					ret |= (TYPE_FLIP | TYPE_GENERAL_SCALE);
				}
			}
			break;
		case (APPLY_TRANSLATE):
			ret = TYPE_TRANSLATION;
			break;
		case (APPLY_IDENTITY):
			break;
		}
		this.type = ret;
	}

	/**
	 * Returns the determinant of the matrix representation of the transform.
	 * The determinant is useful both to determine if the transform can be
	 * inverted and to get a single value representing the combined X and Y
	 * scaling of the transform.
	 * <p>
	 * If the determinant is non-zero, then this transform is invertible and the
	 * various methods that depend on the inverse transform do not need to throw
	 * a {@link NoninvertibleTransformException}. If the determinant is zero
	 * then this transform can not be inverted since the transform maps all
	 * input coordinates onto a line or a point. If the determinant is near
	 * enough to zero then inverse transform operations might not carry enough
	 * precision to produce meaningful results.
	 * <p>
	 * If this transform represents a uniform scale, as indicated by the
	 * <code>getType</code> method then the determinant also represents the
	 * square of the uniform scale factor by which all of the points are
	 * expanded from or contracted towards the origin. If this transform
	 * represents a non-uniform scale or more general transform then the
	 * determinant is not likely to represent a value useful for any purpose
	 * other than determining if inverse transforms are possible.
	 * <p>
	 * Mathematically, the determinant is calculated using the formula:
	 * 
	 * <pre>
	 *          |  m00  m01  m02  |
	 *          |  m10  m11  m12  |  =  m00 * m11 - m01 * m10
	 *          |   0    0    1   |
	 * </pre>
	 * 
	 * @return the determinant of the matrix used to transform the coordinates.
	 * @see #getType
	 * @see #createInverse
	 * @see #inverseTransform
	 * @see #TYPE_UNIFORM_SCALE
	 * @since 1.2
	 */
	@SuppressWarnings("fallthrough")
	public double getDeterminant() {
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
		case (APPLY_SHEAR | APPLY_SCALE):
			return A11 * A22 - A12 * A21;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
		case (APPLY_SHEAR):
			return -(A12 * A21);
		case (APPLY_SCALE | APPLY_TRANSLATE):
		case (APPLY_SCALE):
			return A11 * A22;
		case (APPLY_TRANSLATE):
		case (APPLY_IDENTITY):
			return 1.0;
		}
	}

	/**
	 * Manually recalculates the state of the transform when the matrix changes
	 * too much to predict the effects on the state. The following table
	 * specifies what the various settings of the state field say about the
	 * values of the corresponding matrix element fields. Note that the rules
	 * governing the SCALE fields are slightly different depending on whether
	 * the SHEAR flag is also set.
	 * 
	 * <pre>
	 *                     SCALE            SHEAR          TRANSLATE
	 *                    m00/m11          m01/m10          m02/m12
	 * 
	 * IDENTITY             1.0              0.0              0.0
	 * TRANSLATE (TR)       1.0              0.0          not both 0.0
	 * SCALE (SC)       not both 1.0         0.0              0.0
	 * TR | SC          not both 1.0         0.0          not both 0.0
	 * SHEAR (SH)           0.0          not both 0.0         0.0
	 * TR | SH              0.0          not both 0.0     not both 0.0
	 * SC | SH          not both 0.0     not both 0.0         0.0
	 * TR | SC | SH     not both 0.0     not both 0.0     not both 0.0
	 * </pre>
	 */
	void updateState() {
		if (A12 == 0.0 && A21 == 0.0) {
			if (A11 == 1.0 && A22 == 1.0) {
				if (A13 == 0.0 && A23 == 0.0) {
					state = APPLY_IDENTITY;
					type = TYPE_IDENTITY;
				} else {
					state = APPLY_TRANSLATE;
					type = TYPE_TRANSLATION;
				}
			} else {
				if (A13 == 0.0 && A23 == 0.0) {
					state = APPLY_SCALE;
					type = TYPE_UNKNOWN;
				} else {
					state = (APPLY_SCALE | APPLY_TRANSLATE);
					type = TYPE_UNKNOWN;
				}
			}
		} else {
			if (A11 == 0.0 && A22 == 0.0) {
				if (A13 == 0.0 && A23 == 0.0) {
					state = APPLY_SHEAR;
					type = TYPE_UNKNOWN;
				} else {
					state = (APPLY_SHEAR | APPLY_TRANSLATE);
					type = TYPE_UNKNOWN;
				}
			} else {
				if (A13 == 0.0 && A23 == 0.0) {
					state = (APPLY_SHEAR | APPLY_SCALE);
					type = TYPE_UNKNOWN;
				} else {
					state = (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE);
					type = TYPE_UNKNOWN;
				}
			}
		}
	}

	/*
	 * Convenience method used internally to throw exceptions when a case was
	 * forgotten in a switch statement.
	 */
	void stateError() {
		throw new InternalError("missing case in transform state switch");
	}

	/**
	 * Retrieves the 6 specifiable values in the 3x3 affine transformation
	 * matrix and places them into an array of double precisions values. The
	 * values are stored in the array as
	 * {&nbsp;m00&nbsp;m10&nbsp;m01&nbsp;m11&nbsp;m02&nbsp;m12&nbsp;}. An array
	 * of 4 doubles can also be specified, in which case only the first four
	 * elements representing the non-transform parts of the array are retrieved
	 * and the values are stored into the array as
	 * {&nbsp;m00&nbsp;m10&nbsp;m01&nbsp;m11&nbsp;}
	 * 
	 * @param flatmatrix
	 *            the double array used to store the returned values.
	 * @see #getScaleX
	 * @see #getScaleY
	 * @see #getShearX
	 * @see #getShearY
	 * @see #getTranslateX
	 * @see #getTranslateY
	 * @since 1.2
	 */
	public void getMatrix(double[] flatmatrix) {
		flatmatrix[0] = A11;
		flatmatrix[1] = A21;
		flatmatrix[2] = A12;
		flatmatrix[3] = A22;
		if (flatmatrix.length > 5) {
			flatmatrix[4] = A13;
			flatmatrix[5] = A23;
		}
	}

	/**
	 * Returns the X coordinate scaling element (m00) of the 3x3 affine
	 * transformation matrix.
	 * 
	 * @return a double value that is the X coordinate of the scaling element of
	 *         the affine transformation matrix.
	 * @see #getMatrix
	 * @since 1.2
	 */
	public double getScaleX() {
		return A11;
	}

	/**
	 * Returns the Y coordinate scaling element (m11) of the 3x3 affine
	 * transformation matrix.
	 * 
	 * @return a double value that is the Y coordinate of the scaling element of
	 *         the affine transformation matrix.
	 * @see #getMatrix
	 * @since 1.2
	 */
	public double getScaleY() {
		return A22;
	}

	/**
	 * Returns the X coordinate shearing element (m01) of the 3x3 affine
	 * transformation matrix.
	 * 
	 * @return a double value that is the X coordinate of the shearing element
	 *         of the affine transformation matrix.
	 * @see #getMatrix
	 * @since 1.2
	 */
	public double getShearX() {
		return A12;
	}

	/**
	 * Returns the Y coordinate shearing element (m10) of the 3x3 affine
	 * transformation matrix.
	 * 
	 * @return a double value that is the Y coordinate of the shearing element
	 *         of the affine transformation matrix.
	 * @see #getMatrix
	 * @since 1.2
	 */
	public double getShearY() {
		return A21;
	}

	/**
	 * Returns the X coordinate of the translation element (m02) of the 3x3
	 * affine transformation matrix.
	 * 
	 * @return a double value that is the X coordinate of the translation
	 *         element of the affine transformation matrix.
	 * @see #getMatrix
	 * @since 1.2
	 */
	public double getTranslateX() {
		return A13;
	}

	/**
	 * Returns the Y coordinate of the translation element (m12) of the 3x3
	 * affine transformation matrix.
	 * 
	 * @return a double value that is the Y coordinate of the translation
	 *         element of the affine transformation matrix.
	 * @see #getMatrix
	 * @since 1.2
	 */
	public double getTranslateY() {
		return A23;
	}

	/**
	 * Concatenates this transform with a translation transformation. This is
	 * equivalent to calling concatenate(T), where T is an
	 * <code>AffineTransform</code> represented by the following matrix:
	 * 
	 * <pre>
	 *          [   1    0    tx  ]
	 *          [   0    1    ty  ]
	 *          [   0    0    1   ]
	 * </pre>
	 * 
	 * @param tx
	 *            the distance by which coordinates are translated in the X axis
	 *            direction
	 * @param ty
	 *            the distance by which coordinates are translated in the Y axis
	 *            direction
	 * @since 1.2
	 */
	public void translate(double tx, double ty) {
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
			return;
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			A13 = tx * A11 + ty * A12 + A13;
			A23 = tx * A21 + ty * A22 + A23;
			if (A13 == 0.0 && A23 == 0.0) {
				state = APPLY_SHEAR | APPLY_SCALE;
				if (type != TYPE_UNKNOWN) {
					type -= TYPE_TRANSLATION;
				}
			}
			return;
		case (APPLY_SHEAR | APPLY_SCALE):
			A13 = tx * A11 + ty * A12;
			A23 = tx * A21 + ty * A22;
			if (A13 != 0.0 || A23 != 0.0) {
				state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE;
				type |= TYPE_TRANSLATION;
			}
			return;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
			A13 = ty * A12 + A13;
			A23 = tx * A21 + A23;
			if (A13 == 0.0 && A23 == 0.0) {
				state = APPLY_SHEAR;
				if (type != TYPE_UNKNOWN) {
					type -= TYPE_TRANSLATION;
				}
			}
			return;
		case (APPLY_SHEAR):
			A13 = ty * A12;
			A23 = tx * A21;
			if (A13 != 0.0 || A23 != 0.0) {
				state = APPLY_SHEAR | APPLY_TRANSLATE;
				type |= TYPE_TRANSLATION;
			}
			return;
		case (APPLY_SCALE | APPLY_TRANSLATE):
			A13 = tx * A11 + A13;
			A23 = ty * A22 + A23;
			if (A13 == 0.0 && A23 == 0.0) {
				state = APPLY_SCALE;
				if (type != TYPE_UNKNOWN) {
					type -= TYPE_TRANSLATION;
				}
			}
			return;
		case (APPLY_SCALE):
			A13 = tx * A11;
			A23 = ty * A22;
			if (A13 != 0.0 || A23 != 0.0) {
				state = APPLY_SCALE | APPLY_TRANSLATE;
				type |= TYPE_TRANSLATION;
			}
			return;
		case (APPLY_TRANSLATE):
			A13 = tx + A13;
			A23 = ty + A23;
			if (A13 == 0.0 && A23 == 0.0) {
				state = APPLY_IDENTITY;
				type = TYPE_IDENTITY;
			}
			return;
		case (APPLY_IDENTITY):
			A13 = tx;
			A23 = ty;
			if (tx != 0.0 || ty != 0.0) {
				state = APPLY_TRANSLATE;
				type = TYPE_TRANSLATION;
			}
			return;
		}
	}

	// Utility methods to optimize rotate methods.
	// These tables translate the flags during predictable quadrant
	// rotations where the shear and scale values are swapped and negated.
	private static final int rot90conversion[] = {
	/* IDENTITY => */APPLY_SHEAR,
	/* TRANSLATE (TR) => */APPLY_SHEAR | APPLY_TRANSLATE,
	/* SCALE (SC) => */APPLY_SHEAR,
	/* SC | TR => */APPLY_SHEAR | APPLY_TRANSLATE,
	/* SHEAR (SH) => */APPLY_SCALE,
	/* SH | TR => */APPLY_SCALE | APPLY_TRANSLATE,
	/* SH | SC => */APPLY_SHEAR | APPLY_SCALE,
	/* SH | SC | TR => */APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE, };

	private final void rotate90() {
		double M0 = A11;
		A11 = A12;
		A12 = -M0;
		M0 = A21;
		A21 = A22;
		A22 = -M0;
		int state = rot90conversion[this.state];
		if ((state & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE && A11 == 1.0
				&& A22 == 1.0) {
			state -= APPLY_SCALE;
		}
		this.state = state;
		type = TYPE_UNKNOWN;
	}

	private final void rotate180() {
		A11 = -A11;
		A22 = -A22;
		int state = this.state;
		if ((state & (APPLY_SHEAR)) != 0) {
			// If there was a shear, then this rotation has no
			// effect on the state.
			A12 = -A12;
			A21 = -A21;
		} else {
			// No shear means the SCALE state may toggle when
			// m00 and m11 are negated.
			if (A11 == 1.0 && A22 == 1.0) {
				this.state = state & ~APPLY_SCALE;
			} else {
				this.state = state | APPLY_SCALE;
			}
		}
		type = TYPE_UNKNOWN;
	}

	private final void rotate270() {
		double M0 = A11;
		A11 = -A12;
		A12 = M0;
		M0 = A21;
		A21 = -A22;
		A22 = M0;
		int state = rot90conversion[this.state];
		if ((state & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE && A11 == 1.0
				&& A22 == 1.0) {
			state -= APPLY_SCALE;
		}
		this.state = state;
		type = TYPE_UNKNOWN;
	}

	/**
	 * Concatenates this transform with a rotation transformation. This is
	 * equivalent to calling concatenate(R), where R is an
	 * <code>AffineTransform</code> represented by the following matrix:
	 * 
	 * <pre>
	 *          [   cos(theta)    -sin(theta)    0   ]
	 *          [   sin(theta)     cos(theta)    0   ]
	 *          [       0              0         1   ]
	 * </pre>
	 * 
	 * Rotating by a positive angle theta rotates points on the positive X axis
	 * toward the positive Y axis. Note also the discussion of <a
	 * href="#quadrantapproximation">Handling 90-Degree Rotations</a> above.
	 * 
	 * @param theta
	 *            the angle of rotation measured in radians
	 * @since 1.2
	 */
	public void rotate(double theta) {
		double sin = Math.sin(theta);
		if (sin == 1.0) {
			rotate90();
		} else if (sin == -1.0) {
			rotate270();
		} else {
			double cos = Math.cos(theta);
			if (cos == -1.0) {
				rotate180();
			} else if (cos != 1.0) {
				double M0, M1;
				M0 = A11;
				M1 = A12;
				A11 = cos * M0 + sin * M1;
				A12 = -sin * M0 + cos * M1;
				M0 = A21;
				M1 = A22;
				A21 = cos * M0 + sin * M1;
				A22 = -sin * M0 + cos * M1;
				updateState();
			}
		}
	}

	/**
	 * Concatenates this transform with a transform that rotates coordinates
	 * around an anchor point. This operation is equivalent to translating the
	 * coordinates so that the anchor point is at the origin (S1), then rotating
	 * them about the new origin (S2), and finally translating so that the
	 * intermediate origin is restored to the coordinates of the original anchor
	 * point (S3).
	 * <p>
	 * This operation is equivalent to the following sequence of calls:
	 * 
	 * <pre>
	 * translate(anchorx, anchory); // S3: final translation
	 * rotate(theta); // S2: rotate around anchor
	 * translate(-anchorx, -anchory); // S1: translate anchor to origin
	 * </pre>
	 * 
	 * Rotating by a positive angle theta rotates points on the positive X axis
	 * toward the positive Y axis. Note also the discussion of <a
	 * href="#quadrantapproximation">Handling 90-Degree Rotations</a> above.
	 * 
	 * @param theta
	 *            the angle of rotation measured in radians
	 * @param anchorx
	 *            the X coordinate of the rotation anchor point
	 * @param anchory
	 *            the Y coordinate of the rotation anchor point
	 * @since 1.2
	 */
	public void rotate(double theta, double anchorx, double anchory) {
		// REMIND: Simple for now - optimize later
		translate(anchorx, anchory);
		rotate(theta);
		translate(-anchorx, -anchory);
	}

	/**
	 * Concatenates this transform with a transform that rotates coordinates
	 * according to a rotation vector. All coordinates rotate about the origin
	 * by the same amount. The amount of rotation is such that coordinates along
	 * the former positive X axis will subsequently align with the vector
	 * pointing from the origin to the specified vector coordinates. If both
	 * <code>vecx</code> and <code>vecy</code> are 0.0, no additional rotation
	 * is added to this transform. This operation is equivalent to calling:
	 * 
	 * <pre>
	 * rotate(Math.atan2(vecy, vecx));
	 * </pre>
	 * 
	 * @param vecx
	 *            the X coordinate of the rotation vector
	 * @param vecy
	 *            the Y coordinate of the rotation vector
	 * @since 1.6
	 */
	public void rotate(double vecx, double vecy) {
		if (vecy == 0.0) {
			if (vecx < 0.0) {
				rotate180();
			}
			// If vecx > 0.0 - no rotation
			// If vecx == 0.0 - undefined rotation - treat as no rotation
		} else if (vecx == 0.0) {
			if (vecy > 0.0) {
				rotate90();
			} else { // vecy must be < 0.0
				rotate270();
			}
		} else {
			double len = Math.sqrt(vecx * vecx + vecy * vecy);
			double sin = vecy / len;
			double cos = vecx / len;
			double M0, M1;
			M0 = A11;
			M1 = A12;
			A11 = cos * M0 + sin * M1;
			A12 = -sin * M0 + cos * M1;
			M0 = A21;
			M1 = A22;
			A21 = cos * M0 + sin * M1;
			A22 = -sin * M0 + cos * M1;
			updateState();
		}
	}

	/**
	 * Concatenates this transform with a transform that rotates coordinates
	 * around an anchor point according to a rotation vector. All coordinates
	 * rotate about the specified anchor coordinates by the same amount. The
	 * amount of rotation is such that coordinates along the former positive X
	 * axis will subsequently align with the vector pointing from the origin to
	 * the specified vector coordinates. If both <code>vecx</code> and
	 * <code>vecy</code> are 0.0, the transform is not modified in any way. This
	 * method is equivalent to calling:
	 * 
	 * <pre>
	 * rotate(Math.atan2(vecy, vecx), anchorx, anchory);
	 * </pre>
	 * 
	 * @param vecx
	 *            the X coordinate of the rotation vector
	 * @param vecy
	 *            the Y coordinate of the rotation vector
	 * @param anchorx
	 *            the X coordinate of the rotation anchor point
	 * @param anchory
	 *            the Y coordinate of the rotation anchor point
	 * @since 1.6
	 */
	public void rotate(double vecx, double vecy, double anchorx, double anchory) {
		// REMIND: Simple for now - optimize later
		translate(anchorx, anchory);
		rotate(vecx, vecy);
		translate(-anchorx, -anchory);
	}

	/**
	 * Concatenates this transform with a transform that rotates coordinates by
	 * the specified number of quadrants. This is equivalent to calling:
	 * 
	 * <pre>
	 * rotate(numquadrants * Math.PI / 2.0);
	 * </pre>
	 * 
	 * Rotating by a positive number of quadrants rotates points on the positive
	 * X axis toward the positive Y axis.
	 * 
	 * @param numquadrants
	 *            the number of 90 degree arcs to rotate by
	 * @since 1.6
	 */
	public void quadrantRotate(int numquadrants) {
		switch (numquadrants & 3) {
		case 0:
			break;
		case 1:
			rotate90();
			break;
		case 2:
			rotate180();
			break;
		case 3:
			rotate270();
			break;
		}
	}

	/**
	 * Concatenates this transform with a transform that rotates coordinates by
	 * the specified number of quadrants around the specified anchor point. This
	 * method is equivalent to calling:
	 * 
	 * <pre>
	 * rotate(numquadrants * Math.PI / 2.0, anchorx, anchory);
	 * </pre>
	 * 
	 * Rotating by a positive number of quadrants rotates points on the positive
	 * X axis toward the positive Y axis.
	 * 
	 * @param numquadrants
	 *            the number of 90 degree arcs to rotate by
	 * @param anchorx
	 *            the X coordinate of the rotation anchor point
	 * @param anchory
	 *            the Y coordinate of the rotation anchor point
	 * @since 1.6
	 */
	public void quadrantRotate(int numquadrants, double anchorx, double anchory) {
		switch (numquadrants & 3) {
		case 0:
			return;
		case 1:
			A13 += anchorx * (A11 - A12) + anchory * (A12 + A11);
			A23 += anchorx * (A21 - A22) + anchory * (A22 + A21);
			rotate90();
			break;
		case 2:
			A13 += anchorx * (A11 + A11) + anchory * (A12 + A12);
			A23 += anchorx * (A21 + A21) + anchory * (A22 + A22);
			rotate180();
			break;
		case 3:
			A13 += anchorx * (A11 + A12) + anchory * (A12 - A11);
			A23 += anchorx * (A21 + A22) + anchory * (A22 - A21);
			rotate270();
			break;
		}
		if (A13 == 0.0 && A23 == 0.0) {
			state &= ~APPLY_TRANSLATE;
		} else {
			state |= APPLY_TRANSLATE;
		}
	}

	/**
	 * Concatenates this transform with a scaling transformation. This is
	 * equivalent to calling concatenate(S), where S is an
	 * <code>AffineTransform</code> represented by the following matrix:
	 * 
	 * <pre>
	 *          [   sx   0    0   ]
	 *          [   0    sy   0   ]
	 *          [   0    0    1   ]
	 * </pre>
	 * 
	 * @param sx
	 *            the factor by which coordinates are scaled along the X axis
	 *            direction
	 * @param sy
	 *            the factor by which coordinates are scaled along the Y axis
	 *            direction
	 * @since 1.2
	 */
	@SuppressWarnings("fallthrough")
	public void scale(double sx, double sy) {
		int state = this.state;
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
		case (APPLY_SHEAR | APPLY_SCALE):
			A11 *= sx;
			A22 *= sy;
			/* NOBREAK */
		case (APPLY_SHEAR | APPLY_TRANSLATE):
		case (APPLY_SHEAR):
			A12 *= sy;
			A21 *= sx;
			if (A12 == 0 && A21 == 0) {
				state &= APPLY_TRANSLATE;
				if (A11 == 1.0 && A22 == 1.0) {
					this.type = (state == APPLY_IDENTITY ? TYPE_IDENTITY
							: TYPE_TRANSLATION);
				} else {
					state |= APPLY_SCALE;
					this.type = TYPE_UNKNOWN;
				}
				this.state = state;
			}
			return;
		case (APPLY_SCALE | APPLY_TRANSLATE):
		case (APPLY_SCALE):
			A11 *= sx;
			A22 *= sy;
			if (A11 == 1.0 && A22 == 1.0) {
				this.state = (state &= APPLY_TRANSLATE);
				this.type = (state == APPLY_IDENTITY ? TYPE_IDENTITY
						: TYPE_TRANSLATION);
			} else {
				this.type = TYPE_UNKNOWN;
			}
			return;
		case (APPLY_TRANSLATE):
		case (APPLY_IDENTITY):
			A11 = sx;
			A22 = sy;
			if (sx != 1.0 || sy != 1.0) {
				this.state = state | APPLY_SCALE;
				this.type = TYPE_UNKNOWN;
			}
			return;
		}
	}

	/**
	 * Concatenates this transform with a shearing transformation. This is
	 * equivalent to calling concatenate(SH), where SH is an
	 * <code>AffineTransform</code> represented by the following matrix:
	 * 
	 * <pre>
	 *          [   1   shx   0   ]
	 *          [  shy   1    0   ]
	 *          [   0    0    1   ]
	 * </pre>
	 * 
	 * @param shx
	 *            the multiplier by which coordinates are shifted in the
	 *            direction of the positive X axis as a factor of their Y
	 *            coordinate
	 * @param shy
	 *            the multiplier by which coordinates are shifted in the
	 *            direction of the positive Y axis as a factor of their X
	 *            coordinate
	 * @since 1.2
	 */
	public void shear(double shx, double shy) {
		int state = this.state;
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
			return;
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
		case (APPLY_SHEAR | APPLY_SCALE):
			double M0,
			M1;
			M0 = A11;
			M1 = A12;
			A11 = M0 + M1 * shy;
			A12 = M0 * shx + M1;

			M0 = A21;
			M1 = A22;
			A21 = M0 + M1 * shy;
			A22 = M0 * shx + M1;
			updateState();
			return;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
		case (APPLY_SHEAR):
			A11 = A12 * shy;
			A22 = A21 * shx;
			if (A11 != 0.0 || A22 != 0.0) {
				this.state = state | APPLY_SCALE;
			}
			this.type = TYPE_UNKNOWN;
			return;
		case (APPLY_SCALE | APPLY_TRANSLATE):
		case (APPLY_SCALE):
			A12 = A11 * shx;
			A21 = A22 * shy;
			if (A12 != 0.0 || A21 != 0.0) {
				this.state = state | APPLY_SHEAR;
			}
			this.type = TYPE_UNKNOWN;
			return;
		case (APPLY_TRANSLATE):
		case (APPLY_IDENTITY):
			A12 = shx;
			A21 = shy;
			if (A12 != 0.0 || A21 != 0.0) {
				this.state = state | APPLY_SCALE | APPLY_SHEAR;
				this.type = TYPE_UNKNOWN;
			}
			return;
		}
	}

	/**
	 * Resets this transform to the Identity transform.
	 * 
	 * @since 1.2
	 */
	public void setToIdentity() {
		A11 = A22 = 1.0;
		A21 = A12 = A13 = A23 = 0.0;
		state = APPLY_IDENTITY;
		type = TYPE_IDENTITY;
	}

	/**
	 * Sets this transform to a translation transformation. The matrix
	 * representing this transform becomes:
	 * 
	 * <pre>
	 *          [   1    0    tx  ]
	 *          [   0    1    ty  ]
	 *          [   0    0    1   ]
	 * </pre>
	 * 
	 * @param tx
	 *            the distance by which coordinates are translated in the X axis
	 *            direction
	 * @param ty
	 *            the distance by which coordinates are translated in the Y axis
	 *            direction
	 * @since 1.2
	 */
	public void setToTranslation(double tx, double ty) {
		A11 = 1.0;
		A21 = 0.0;
		A12 = 0.0;
		A22 = 1.0;
		A13 = tx;
		A23 = ty;
		if (tx != 0.0 || ty != 0.0) {
			state = APPLY_TRANSLATE;
			type = TYPE_TRANSLATION;
		} else {
			state = APPLY_IDENTITY;
			type = TYPE_IDENTITY;
		}
	}

	/**
	 * Sets this transform to a rotation transformation. The matrix representing
	 * this transform becomes:
	 * 
	 * <pre>
	 *          [   cos(theta)    -sin(theta)    0   ]
	 *          [   sin(theta)     cos(theta)    0   ]
	 *          [       0              0         1   ]
	 * </pre>
	 * 
	 * Rotating by a positive angle theta rotates points on the positive X axis
	 * toward the positive Y axis. Note also the discussion of <a
	 * href="#quadrantapproximation">Handling 90-Degree Rotations</a> above.
	 * 
	 * @param theta
	 *            the angle of rotation measured in radians
	 * @since 1.2
	 */
	public void setToRotation(double theta) {
		double sin = Math.sin(theta);
		double cos;
		if (sin == 1.0 || sin == -1.0) {
			cos = 0.0;
			state = APPLY_SHEAR;
			type = TYPE_QUADRANT_ROTATION;
		} else {
			cos = Math.cos(theta);
			if (cos == -1.0) {
				sin = 0.0;
				state = APPLY_SCALE;
				type = TYPE_QUADRANT_ROTATION;
			} else if (cos == 1.0) {
				sin = 0.0;
				state = APPLY_IDENTITY;
				type = TYPE_IDENTITY;
			} else {
				state = APPLY_SHEAR | APPLY_SCALE;
				type = TYPE_GENERAL_ROTATION;
			}
		}
		A11 = cos;
		A21 = sin;
		A12 = -sin;
		A22 = cos;
		A13 = 0.0;
		A23 = 0.0;
	}

	/**
	 * Sets this transform to a translated rotation transformation. This
	 * operation is equivalent to translating the coordinates so that the anchor
	 * point is at the origin (S1), then rotating them about the new origin
	 * (S2), and finally translating so that the intermediate origin is restored
	 * to the coordinates of the original anchor point (S3).
	 * <p>
	 * This operation is equivalent to the following sequence of calls:
	 * 
	 * <pre>
	 * setToTranslation(anchorx, anchory); // S3: final translation
	 * rotate(theta); // S2: rotate around anchor
	 * translate(-anchorx, -anchory); // S1: translate anchor to origin
	 * </pre>
	 * 
	 * The matrix representing this transform becomes:
	 * 
	 * <pre>
	 *          [   cos(theta)    -sin(theta)    x-x*cos+y*sin  ]
	 *          [   sin(theta)     cos(theta)    y-x*sin-y*cos  ]
	 *          [       0              0               1        ]
	 * </pre>
	 * 
	 * Rotating by a positive angle theta rotates points on the positive X axis
	 * toward the positive Y axis. Note also the discussion of <a
	 * href="#quadrantapproximation">Handling 90-Degree Rotations</a> above.
	 * 
	 * @param theta
	 *            the angle of rotation measured in radians
	 * @param anchorx
	 *            the X coordinate of the rotation anchor point
	 * @param anchory
	 *            the Y coordinate of the rotation anchor point
	 * @since 1.2
	 */
	public void setToRotation(double theta, double anchorx, double anchory) {
		setToRotation(theta);
		double sin = A21;
		double oneMinusCos = 1.0 - A11;
		A13 = anchorx * oneMinusCos + anchory * sin;
		A23 = anchory * oneMinusCos - anchorx * sin;
		if (A13 != 0.0 || A23 != 0.0) {
			state |= APPLY_TRANSLATE;
			type |= TYPE_TRANSLATION;
		}
	}

	/**
	 * Sets this transform to a rotation transformation that rotates coordinates
	 * according to a rotation vector. All coordinates rotate about the origin
	 * by the same amount. The amount of rotation is such that coordinates along
	 * the former positive X axis will subsequently align with the vector
	 * pointing from the origin to the specified vector coordinates. If both
	 * <code>vecx</code> and <code>vecy</code> are 0.0, the transform is set to
	 * an identity transform. This operation is equivalent to calling:
	 * 
	 * <pre>
	 * setToRotation(Math.atan2(vecy, vecx));
	 * </pre>
	 * 
	 * @param vecx
	 *            the X coordinate of the rotation vector
	 * @param vecy
	 *            the Y coordinate of the rotation vector
	 * @since 1.6
	 */
	public void setToRotation(double vecx, double vecy) {
		double sin, cos;
		if (vecy == 0) {
			sin = 0.0;
			if (vecx < 0.0) {
				cos = -1.0;
				state = APPLY_SCALE;
				type = TYPE_QUADRANT_ROTATION;
			} else {
				cos = 1.0;
				state = APPLY_IDENTITY;
				type = TYPE_IDENTITY;
			}
		} else if (vecx == 0) {
			cos = 0.0;
			sin = (vecy > 0.0) ? 1.0 : -1.0;
			state = APPLY_SHEAR;
			type = TYPE_QUADRANT_ROTATION;
		} else {
			double len = Math.sqrt(vecx * vecx + vecy * vecy);
			cos = vecx / len;
			sin = vecy / len;
			state = APPLY_SHEAR | APPLY_SCALE;
			type = TYPE_GENERAL_ROTATION;
		}
		A11 = cos;
		A21 = sin;
		A12 = -sin;
		A22 = cos;
		A13 = 0.0;
		A23 = 0.0;
	}

	/**
	 * Sets this transform to a rotation transformation that rotates coordinates
	 * around an anchor point according to a rotation vector. All coordinates
	 * rotate about the specified anchor coordinates by the same amount. The
	 * amount of rotation is such that coordinates along the former positive X
	 * axis will subsequently align with the vector pointing from the origin to
	 * the specified vector coordinates. If both <code>vecx</code> and
	 * <code>vecy</code> are 0.0, the transform is set to an identity transform.
	 * This operation is equivalent to calling:
	 * 
	 * <pre>
	 * setToTranslation(Math.atan2(vecy, vecx), anchorx, anchory);
	 * </pre>
	 * 
	 * @param vecx
	 *            the X coordinate of the rotation vector
	 * @param vecy
	 *            the Y coordinate of the rotation vector
	 * @param anchorx
	 *            the X coordinate of the rotation anchor point
	 * @param anchory
	 *            the Y coordinate of the rotation anchor point
	 * @since 1.6
	 */
	public void setToRotation(double vecx, double vecy, double anchorx,
			double anchory) {
		setToRotation(vecx, vecy);
		double sin = A21;
		double oneMinusCos = 1.0 - A11;
		A13 = anchorx * oneMinusCos + anchory * sin;
		A23 = anchory * oneMinusCos - anchorx * sin;
		if (A13 != 0.0 || A23 != 0.0) {
			state |= APPLY_TRANSLATE;
			type |= TYPE_TRANSLATION;
		}
	}

	/**
	 * Sets this transform to a rotation transformation that rotates coordinates
	 * by the specified number of quadrants. This operation is equivalent to
	 * calling:
	 * 
	 * <pre>
	 * setToRotation(numquadrants * Math.PI / 2.0);
	 * </pre>
	 * 
	 * Rotating by a positive number of quadrants rotates points on the positive
	 * X axis toward the positive Y axis.
	 * 
	 * @param numquadrants
	 *            the number of 90 degree arcs to rotate by
	 * @since 1.6
	 */
	public void setToQuadrantRotation(int numquadrants) {
		switch (numquadrants & 3) {
		case 0:
			A11 = 1.0;
			A21 = 0.0;
			A12 = 0.0;
			A22 = 1.0;
			A13 = 0.0;
			A23 = 0.0;
			state = APPLY_IDENTITY;
			type = TYPE_IDENTITY;
			break;
		case 1:
			A11 = 0.0;
			A21 = 1.0;
			A12 = -1.0;
			A22 = 0.0;
			A13 = 0.0;
			A23 = 0.0;
			state = APPLY_SHEAR;
			type = TYPE_QUADRANT_ROTATION;
			break;
		case 2:
			A11 = -1.0;
			A21 = 0.0;
			A12 = 0.0;
			A22 = -1.0;
			A13 = 0.0;
			A23 = 0.0;
			state = APPLY_SCALE;
			type = TYPE_QUADRANT_ROTATION;
			break;
		case 3:
			A11 = 0.0;
			A21 = -1.0;
			A12 = 1.0;
			A22 = 0.0;
			A13 = 0.0;
			A23 = 0.0;
			state = APPLY_SHEAR;
			type = TYPE_QUADRANT_ROTATION;
			break;
		}
	}

	/**
	 * Sets this transform to a translated rotation transformation that rotates
	 * coordinates by the specified number of quadrants around the specified
	 * anchor point. This operation is equivalent to calling:
	 * 
	 * <pre>
	 * setToRotation(numquadrants * Math.PI / 2.0, anchorx, anchory);
	 * </pre>
	 * 
	 * Rotating by a positive number of quadrants rotates points on the positive
	 * X axis toward the positive Y axis.
	 * 
	 * @param numquadrants
	 *            the number of 90 degree arcs to rotate by
	 * @param anchorx
	 *            the X coordinate of the rotation anchor point
	 * @param anchory
	 *            the Y coordinate of the rotation anchor point
	 * @since 1.6
	 */
	public void setToQuadrantRotation(int numquadrants, double anchorx,
			double anchory) {
		switch (numquadrants & 3) {
		case 0:
			A11 = 1.0;
			A21 = 0.0;
			A12 = 0.0;
			A22 = 1.0;
			A13 = 0.0;
			A23 = 0.0;
			state = APPLY_IDENTITY;
			type = TYPE_IDENTITY;
			break;
		case 1:
			A11 = 0.0;
			A21 = 1.0;
			A12 = -1.0;
			A22 = 0.0;
			A13 = anchorx + anchory;
			A23 = anchory - anchorx;
			if (A13 == 0.0 && A23 == 0.0) {
				state = APPLY_SHEAR;
				type = TYPE_QUADRANT_ROTATION;
			} else {
				state = APPLY_SHEAR | APPLY_TRANSLATE;
				type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION;
			}
			break;
		case 2:
			A11 = -1.0;
			A21 = 0.0;
			A12 = 0.0;
			A22 = -1.0;
			A13 = anchorx + anchorx;
			A23 = anchory + anchory;
			if (A13 == 0.0 && A23 == 0.0) {
				state = APPLY_SCALE;
				type = TYPE_QUADRANT_ROTATION;
			} else {
				state = APPLY_SCALE | APPLY_TRANSLATE;
				type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION;
			}
			break;
		case 3:
			A11 = 0.0;
			A21 = -1.0;
			A12 = 1.0;
			A22 = 0.0;
			A13 = anchorx - anchory;
			A23 = anchory + anchorx;
			if (A13 == 0.0 && A23 == 0.0) {
				state = APPLY_SHEAR;
				type = TYPE_QUADRANT_ROTATION;
			} else {
				state = APPLY_SHEAR | APPLY_TRANSLATE;
				type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION;
			}
			break;
		}
	}

	/**
	 * Sets this transform to a scaling transformation. The matrix representing
	 * this transform becomes:
	 * 
	 * <pre>
	 *          [   sx   0    0   ]
	 *          [   0    sy   0   ]
	 *          [   0    0    1   ]
	 * </pre>
	 * 
	 * @param sx
	 *            the factor by which coordinates are scaled along the X axis
	 *            direction
	 * @param sy
	 *            the factor by which coordinates are scaled along the Y axis
	 *            direction
	 * @since 1.2
	 */
	public void setToScale(double sx, double sy) {
		A11 = sx;
		A21 = 0.0;
		A12 = 0.0;
		A22 = sy;
		A13 = 0.0;
		A23 = 0.0;
		if (sx != 1.0 || sy != 1.0) {
			state = APPLY_SCALE;
			type = TYPE_UNKNOWN;
		} else {
			state = APPLY_IDENTITY;
			type = TYPE_IDENTITY;
		}
	}

	/**
	 * Sets this transform to a shearing transformation. The matrix representing
	 * this transform becomes:
	 * 
	 * <pre>
	 *          [   1   shx   0   ]
	 *          [  shy   1    0   ]
	 *          [   0    0    1   ]
	 * </pre>
	 * 
	 * @param shx
	 *            the multiplier by which coordinates are shifted in the
	 *            direction of the positive X axis as a factor of their Y
	 *            coordinate
	 * @param shy
	 *            the multiplier by which coordinates are shifted in the
	 *            direction of the positive Y axis as a factor of their X
	 *            coordinate
	 * @since 1.2
	 */
	public void setToShear(double shx, double shy) {
		A11 = 1.0;
		A12 = shx;
		A21 = shy;
		A22 = 1.0;
		A13 = 0.0;
		A23 = 0.0;
		if (shx != 0.0 || shy != 0.0) {
			state = (APPLY_SHEAR | APPLY_SCALE);
			type = TYPE_UNKNOWN;
		} else {
			state = APPLY_IDENTITY;
			type = TYPE_IDENTITY;
		}
	}

	/**
	 * Sets this transform to a copy of the transform in the specified
	 * <code>AffineTransform</code> object.
	 * 
	 * @param Tx
	 *            the <code>AffineTransform</code> object from which to copy the
	 *            transform
	 * @since 1.2
	 */
	public void setTransform(CopyOfAffineTransform Tx) {
		this.A11 = Tx.A11;
		this.A21 = Tx.A21;
		this.A12 = Tx.A12;
		this.A22 = Tx.A22;
		this.A13 = Tx.A13;
		this.A23 = Tx.A23;
		this.state = Tx.state;
		this.type = Tx.type;
	}

	/**
	 * Sets this transform to the matrix specified by the 6 double precision
	 * values.
	 * 
	 * @param m00
	 *            the X coordinate scaling element of the 3x3 matrix
	 * @param m10
	 *            the Y coordinate shearing element of the 3x3 matrix
	 * @param m01
	 *            the X coordinate shearing element of the 3x3 matrix
	 * @param m11
	 *            the Y coordinate scaling element of the 3x3 matrix
	 * @param m02
	 *            the X coordinate translation element of the 3x3 matrix
	 * @param m12
	 *            the Y coordinate translation element of the 3x3 matrix
	 * @since 1.2
	 */
	public void setTransform(double m00, double m10, double m01, double m11,
			double m02, double m12) {
		this.A11 = m00;
		this.A21 = m10;
		this.A12 = m01;
		this.A22 = m11;
		this.A13 = m02;
		this.A23 = m12;
		updateState();
	}

	/**
	 * Concatenates an <code>AffineTransform</code> <code>Tx</code> to this
	 * <code>AffineTransform</code> Cx in the most commonly useful way to
	 * provide a new user space that is mapped to the former user space by
	 * <code>Tx</code>. Cx is updated to perform the combined transformation.
	 * Transforming a point p by the updated transform Cx' is equivalent to
	 * first transforming p by <code>Tx</code> and then transforming the result
	 * by the original transform Cx like this: Cx'(p) = Cx(Tx(p)) In matrix
	 * notation, if this transform Cx is represented by the matrix [this] and
	 * <code>Tx</code> is represented by the matrix [Tx] then this method does
	 * the following:
	 * 
	 * <pre>
	 *          [this] = [this] x [Tx]
	 * </pre>
	 * 
	 * @param Tx
	 *            the <code>AffineTransform</code> object to be concatenated
	 *            with this <code>AffineTransform</code> object.
	 * @see #preConcatenate
	 * @since 1.2
	 */
	@SuppressWarnings("fallthrough")
	public void concatenate(CopyOfAffineTransform Tx) {
		double M0, M1;
		double T00, T01, T10, T11;
		double T02, T12;
		int mystate = state;
		int txstate = Tx.state;
		switch ((txstate << HI_SHIFT) | mystate) {

		/* ---------- Tx == IDENTITY cases ---------- */
		case (HI_IDENTITY | APPLY_IDENTITY):
		case (HI_IDENTITY | APPLY_TRANSLATE):
		case (HI_IDENTITY | APPLY_SCALE):
		case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):
		case (HI_IDENTITY | APPLY_SHEAR):
		case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):
		case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):
		case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			return;

			/* ---------- this == IDENTITY cases ---------- */
		case (HI_SHEAR | HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):
			A12 = Tx.A12;
			A21 = Tx.A21;
			/* NOBREAK */
		case (HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):
			A11 = Tx.A11;
			A22 = Tx.A22;
			/* NOBREAK */
		case (HI_TRANSLATE | APPLY_IDENTITY):
			A13 = Tx.A13;
			A23 = Tx.A23;
			state = txstate;
			type = Tx.type;
			return;
		case (HI_SHEAR | HI_SCALE | APPLY_IDENTITY):
			A12 = Tx.A12;
			A21 = Tx.A21;
			/* NOBREAK */
		case (HI_SCALE | APPLY_IDENTITY):
			A11 = Tx.A11;
			A22 = Tx.A22;
			state = txstate;
			type = Tx.type;
			return;
		case (HI_SHEAR | HI_TRANSLATE | APPLY_IDENTITY):
			A13 = Tx.A13;
			A23 = Tx.A23;
			/* NOBREAK */
		case (HI_SHEAR | APPLY_IDENTITY):
			A12 = Tx.A12;
			A21 = Tx.A21;
			A11 = A22 = 0.0;
			state = txstate;
			type = Tx.type;
			return;

			/* ---------- Tx == TRANSLATE cases ---------- */
		case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
		case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):
		case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):
		case (HI_TRANSLATE | APPLY_SHEAR):
		case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):
		case (HI_TRANSLATE | APPLY_SCALE):
		case (HI_TRANSLATE | APPLY_TRANSLATE):
			translate(Tx.A13, Tx.A23);
			return;

			/* ---------- Tx == SCALE cases ---------- */
		case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
		case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):
		case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):
		case (HI_SCALE | APPLY_SHEAR):
		case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):
		case (HI_SCALE | APPLY_SCALE):
		case (HI_SCALE | APPLY_TRANSLATE):
			scale(Tx.A11, Tx.A22);
			return;

			/* ---------- Tx == SHEAR cases ---------- */
		case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
		case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):
			T01 = Tx.A12;
			T10 = Tx.A21;
			M0 = A11;
			A11 = A12 * T10;
			A12 = M0 * T01;
			M0 = A21;
			A21 = A22 * T10;
			A22 = M0 * T01;
			type = TYPE_UNKNOWN;
			return;
		case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):
		case (HI_SHEAR | APPLY_SHEAR):
			A11 = A12 * Tx.A21;
			A12 = 0.0;
			A22 = A21 * Tx.A12;
			A21 = 0.0;
			state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);
			type = TYPE_UNKNOWN;
			return;
		case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
		case (HI_SHEAR | APPLY_SCALE):
			A12 = A11 * Tx.A12;
			A11 = 0.0;
			A21 = A22 * Tx.A21;
			A22 = 0.0;
			state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);
			type = TYPE_UNKNOWN;
			return;
		case (HI_SHEAR | APPLY_TRANSLATE):
			A11 = 0.0;
			A12 = Tx.A12;
			A21 = Tx.A21;
			A22 = 0.0;
			state = APPLY_TRANSLATE | APPLY_SHEAR;
			type = TYPE_UNKNOWN;
			return;
		}
		// If Tx has more than one attribute, it is not worth optimizing
		// all of those cases...
		T00 = Tx.A11;
		T01 = Tx.A12;
		T02 = Tx.A13;
		T10 = Tx.A21;
		T11 = Tx.A22;
		T12 = Tx.A23;
		switch (mystate) {
		default:
			stateError();
			/* NOTREACHED */
		case (APPLY_SHEAR | APPLY_SCALE):
			state = mystate | txstate;
			/* NOBREAK */
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			M0 = A11;
			M1 = A12;
			A11 = T00 * M0 + T10 * M1;
			A12 = T01 * M0 + T11 * M1;
			A13 += T02 * M0 + T12 * M1;

			M0 = A21;
			M1 = A22;
			A21 = T00 * M0 + T10 * M1;
			A22 = T01 * M0 + T11 * M1;
			A23 += T02 * M0 + T12 * M1;
			type = TYPE_UNKNOWN;
			return;

		case (APPLY_SHEAR | APPLY_TRANSLATE):
		case (APPLY_SHEAR):
			M0 = A12;
			A11 = T10 * M0;
			A12 = T11 * M0;
			A13 += T12 * M0;

			M0 = A21;
			A21 = T00 * M0;
			A22 = T01 * M0;
			A23 += T02 * M0;
			break;

		case (APPLY_SCALE | APPLY_TRANSLATE):
		case (APPLY_SCALE):
			M0 = A11;
			A11 = T00 * M0;
			A12 = T01 * M0;
			A13 += T02 * M0;

			M0 = A22;
			A21 = T10 * M0;
			A22 = T11 * M0;
			A23 += T12 * M0;
			break;

		case (APPLY_TRANSLATE):
			A11 = T00;
			A12 = T01;
			A13 += T02;

			A21 = T10;
			A22 = T11;
			A23 += T12;
			state = txstate | APPLY_TRANSLATE;
			type = TYPE_UNKNOWN;
			return;
		}
		updateState();
	}

	/**
	 * Concatenates an <code>AffineTransform</code> <code>Tx</code> to this
	 * <code>AffineTransform</code> Cx in a less commonly used way such that
	 * <code>Tx</code> modifies the coordinate transformation relative to the
	 * absolute pixel space rather than relative to the existing user space. Cx
	 * is updated to perform the combined transformation. Transforming a point p
	 * by the updated transform Cx' is equivalent to first transforming p by the
	 * original transform Cx and then transforming the result by <code>Tx</code>
	 * like this: Cx'(p) = Tx(Cx(p)) In matrix notation, if this transform Cx is
	 * represented by the matrix [this] and <code>Tx</code> is represented by
	 * the matrix [Tx] then this method does the following:
	 * 
	 * <pre>
	 *          [this] = [Tx] x [this]
	 * </pre>
	 * 
	 * @param Tx
	 *            the <code>AffineTransform</code> object to be concatenated
	 *            with this <code>AffineTransform</code> object.
	 * @see #concatenate
	 * @since 1.2
	 */
	@SuppressWarnings("fallthrough")
	public void preConcatenate(CopyOfAffineTransform Tx) {
		double M0, M1;
		double T00, T01, T10, T11;
		double T02, T12;
		int mystate = state;
		int txstate = Tx.state;
		switch ((txstate << HI_SHIFT) | mystate) {
		case (HI_IDENTITY | APPLY_IDENTITY):
		case (HI_IDENTITY | APPLY_TRANSLATE):
		case (HI_IDENTITY | APPLY_SCALE):
		case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):
		case (HI_IDENTITY | APPLY_SHEAR):
		case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):
		case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):
		case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			// Tx is IDENTITY...
			return;

		case (HI_TRANSLATE | APPLY_IDENTITY):
		case (HI_TRANSLATE | APPLY_SCALE):
		case (HI_TRANSLATE | APPLY_SHEAR):
		case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):
			// Tx is TRANSLATE, this has no TRANSLATE
			A13 = Tx.A13;
			A23 = Tx.A23;
			state = mystate | APPLY_TRANSLATE;
			type |= TYPE_TRANSLATION;
			return;

		case (HI_TRANSLATE | APPLY_TRANSLATE):
		case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):
		case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):
		case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			// Tx is TRANSLATE, this has one too
			A13 = A13 + Tx.A13;
			A23 = A23 + Tx.A23;
			return;

		case (HI_SCALE | APPLY_TRANSLATE):
		case (HI_SCALE | APPLY_IDENTITY):
			// Only these two existing states need a new state
			state = mystate | APPLY_SCALE;
			/* NOBREAK */
		case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
		case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):
		case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):
		case (HI_SCALE | APPLY_SHEAR):
		case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):
		case (HI_SCALE | APPLY_SCALE):
			// Tx is SCALE, this is anything
			T00 = Tx.A11;
			T11 = Tx.A22;
			if ((mystate & APPLY_SHEAR) != 0) {
				A12 = A12 * T00;
				A21 = A21 * T11;
				if ((mystate & APPLY_SCALE) != 0) {
					A11 = A11 * T00;
					A22 = A22 * T11;
				}
			} else {
				A11 = A11 * T00;
				A22 = A22 * T11;
			}
			if ((mystate & APPLY_TRANSLATE) != 0) {
				A13 = A13 * T00;
				A23 = A23 * T11;
			}
			type = TYPE_UNKNOWN;
			return;
		case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):
		case (HI_SHEAR | APPLY_SHEAR):
			mystate = mystate | APPLY_SCALE;
			/* NOBREAK */
		case (HI_SHEAR | APPLY_TRANSLATE):
		case (HI_SHEAR | APPLY_IDENTITY):
		case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
		case (HI_SHEAR | APPLY_SCALE):
			state = mystate ^ APPLY_SHEAR;
			/* NOBREAK */
		case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
		case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):
			// Tx is SHEAR, this is anything
			T01 = Tx.A12;
			T10 = Tx.A21;

			M0 = A11;
			A11 = A21 * T01;
			A21 = M0 * T10;

			M0 = A12;
			A12 = A22 * T01;
			A22 = M0 * T10;

			M0 = A13;
			A13 = A23 * T01;
			A23 = M0 * T10;
			type = TYPE_UNKNOWN;
			return;
		}
		// If Tx has more than one attribute, it is not worth optimizing
		// all of those cases...
		T00 = Tx.A11;
		T01 = Tx.A12;
		T02 = Tx.A13;
		T10 = Tx.A21;
		T11 = Tx.A22;
		T12 = Tx.A23;
		switch (mystate) {
		default:
			stateError();
			/* NOTREACHED */
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			M0 = A13;
			M1 = A23;
			T02 += M0 * T00 + M1 * T01;
			T12 += M0 * T10 + M1 * T11;

			/* NOBREAK */
		case (APPLY_SHEAR | APPLY_SCALE):
			A13 = T02;
			A23 = T12;

			M0 = A11;
			M1 = A21;
			A11 = M0 * T00 + M1 * T01;
			A21 = M0 * T10 + M1 * T11;

			M0 = A12;
			M1 = A22;
			A12 = M0 * T00 + M1 * T01;
			A22 = M0 * T10 + M1 * T11;
			break;

		case (APPLY_SHEAR | APPLY_TRANSLATE):
			M0 = A13;
			M1 = A23;
			T02 += M0 * T00 + M1 * T01;
			T12 += M0 * T10 + M1 * T11;

			/* NOBREAK */
		case (APPLY_SHEAR):
			A13 = T02;
			A23 = T12;

			M0 = A21;
			A11 = M0 * T01;
			A21 = M0 * T11;

			M0 = A12;
			A12 = M0 * T00;
			A22 = M0 * T10;
			break;

		case (APPLY_SCALE | APPLY_TRANSLATE):
			M0 = A13;
			M1 = A23;
			T02 += M0 * T00 + M1 * T01;
			T12 += M0 * T10 + M1 * T11;

			/* NOBREAK */
		case (APPLY_SCALE):
			A13 = T02;
			A23 = T12;

			M0 = A11;
			A11 = M0 * T00;
			A21 = M0 * T10;

			M0 = A22;
			A12 = M0 * T01;
			A22 = M0 * T11;
			break;

		case (APPLY_TRANSLATE):
			M0 = A13;
			M1 = A23;
			T02 += M0 * T00 + M1 * T01;
			T12 += M0 * T10 + M1 * T11;

			/* NOBREAK */
		case (APPLY_IDENTITY):
			A13 = T02;
			A23 = T12;

			A11 = T00;
			A21 = T10;

			A12 = T01;
			A22 = T11;

			state = mystate | txstate;
			type = TYPE_UNKNOWN;
			return;
		}
		updateState();
	}

	/**
	 * Returns an <code>AffineTransform</code> object representing the inverse
	 * transformation. The inverse transform Tx' of this transform Tx maps
	 * coordinates transformed by Tx back to their original coordinates. In
	 * other words, Tx'(Tx(p)) = p = Tx(Tx'(p)).
	 * <p>
	 * If this transform maps all coordinates onto a point or a line then it
	 * will not have an inverse, since coordinates that do not lie on the
	 * destination point or line will not have an inverse mapping. The
	 * <code>getDeterminant</code> method can be used to determine if this
	 * transform has no inverse, in which case an exception will be thrown if
	 * the <code>createInverse</code> method is called.
	 * 
	 * @return a new <code>AffineTransform</code> object representing the
	 *         inverse transformation.
	 * @see #getDeterminant
	 * @exception NoninvertibleTransformException
	 *                if the matrix cannot be inverted.
	 * @since 1.2
	 */
	public CopyOfAffineTransform createInverse()

	{
		double det;
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
			return null;
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			det = A11 * A22 - A12 * A21;
			if (Math.abs(det) <= Double.MIN_VALUE) {
				// throw new NoninvertibleTransformException("Determinant is "+
				// det);
				return null;
			}
			return new CopyOfAffineTransform(A22 / det, -A21 / det, -A12 / det, A11
					/ det, (A12 * A23 - A22 * A13) / det, (A21 * A13 - A11
					* A23)
					/ det, (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE));
		case (APPLY_SHEAR | APPLY_SCALE):
			det = A11 * A22 - A12 * A21;
			if (Math.abs(det) <= Double.MIN_VALUE) {
				// throw new NoninvertibleTransformException("Determinant is "+
				// det);
				return null;
			}
			return new CopyOfAffineTransform(A22 / det, -A21 / det, -A12 / det, A11
					/ det, 0.0, 0.0, (APPLY_SHEAR | APPLY_SCALE));
		case (APPLY_SHEAR | APPLY_TRANSLATE):
			if (A12 == 0.0 || A21 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return null;
			}
			return new CopyOfAffineTransform(0.0, 1.0 / A12, 1.0 / A21, 0.0, -A23
					/ A21, -A13 / A12, (APPLY_SHEAR | APPLY_TRANSLATE));
		case (APPLY_SHEAR):
			if (A12 == 0.0 || A21 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return null;
			}
			return new CopyOfAffineTransform(0.0, 1.0 / A12, 1.0 / A21, 0.0, 0.0,
					0.0, (APPLY_SHEAR));
		case (APPLY_SCALE | APPLY_TRANSLATE):
			if (A11 == 0.0 || A22 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return null;
			}
			return new CopyOfAffineTransform(1.0 / A11, 0.0, 0.0, 1.0 / A22, -A13
					/ A11, -A23 / A22, (APPLY_SCALE | APPLY_TRANSLATE));
		case (APPLY_SCALE):
			if (A11 == 0.0 || A22 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return null;
			}
			return new CopyOfAffineTransform(1.0 / A11, 0.0, 0.0, 1.0 / A22, 0.0,
					0.0, (APPLY_SCALE));
		case (APPLY_TRANSLATE):
			return new CopyOfAffineTransform(1.0, 0.0, 0.0, 1.0, -A13, -A23,
					(APPLY_TRANSLATE));
		case (APPLY_IDENTITY):
			return new CopyOfAffineTransform();
		}

		/* NOTREACHED */
	}

	/**
	 * Sets this transform to the inverse of itself. The inverse transform Tx'
	 * of this transform Tx maps coordinates transformed by Tx back to their
	 * original coordinates. In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)).
	 * <p>
	 * If this transform maps all coordinates onto a point or a line then it
	 * will not have an inverse, since coordinates that do not lie on the
	 * destination point or line will not have an inverse mapping. The
	 * <code>getDeterminant</code> method can be used to determine if this
	 * transform has no inverse, in which case an exception will be thrown if
	 * the <code>invert</code> method is called.
	 * 
	 * @see #getDeterminant
	 * @exception NoninvertibleTransformException
	 *                if the matrix cannot be inverted.
	 * @since 1.6
	 */
	public boolean invert()

	{
		double M00, M01, M02;
		double M10, M11, M12;
		double det;
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
			return true;
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			M00 = A11;
			M01 = A12;
			M02 = A13;
			M10 = A21;
			M11 = A22;
			M12 = A23;
			det = M00 * M11 - M01 * M10;
			if (Math.abs(det) <= Double.MIN_VALUE) {
				// throw new NoninvertibleTransformException("Determinant is "+
				// det);
				return false;
			}
			A11 = M11 / det;
			A21 = -M10 / det;
			A12 = -M01 / det;
			A22 = M00 / det;
			A13 = (M01 * M12 - M11 * M02) / det;
			A23 = (M10 * M02 - M00 * M12) / det;
			break;
		case (APPLY_SHEAR | APPLY_SCALE):
			M00 = A11;
			M01 = A12;
			M10 = A21;
			M11 = A22;
			det = M00 * M11 - M01 * M10;
			if (Math.abs(det) <= Double.MIN_VALUE) {
				// throw new NoninvertibleTransformException("Determinant is "+
				// det);
				return false;
			}
			A11 = M11 / det;
			A21 = -M10 / det;
			A12 = -M01 / det;
			A22 = M00 / det;
			// m02 = 0.0;
			// m12 = 0.0;
			break;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
			M01 = A12;
			M02 = A13;
			M10 = A21;
			M12 = A23;
			if (M01 == 0.0 || M10 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return false;
			}
			// m00 = 0.0;
			A21 = 1.0 / M01;
			A12 = 1.0 / M10;
			// m11 = 0.0;
			A13 = -M12 / M10;
			A23 = -M02 / M01;
			break;
		case (APPLY_SHEAR):
			M01 = A12;
			M10 = A21;
			if (M01 == 0.0 || M10 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return false;
			}
			// m00 = 0.0;
			A21 = 1.0 / M01;
			A12 = 1.0 / M10;
			// m11 = 0.0;
			// m02 = 0.0;
			// m12 = 0.0;
			break;
		case (APPLY_SCALE | APPLY_TRANSLATE):
			M00 = A11;
			M02 = A13;
			M11 = A22;
			M12 = A23;
			if (M00 == 0.0 || M11 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return false;
			}
			A11 = 1.0 / M00;
			// m10 = 0.0;
			// m01 = 0.0;
			A22 = 1.0 / M11;
			A13 = -M02 / M00;
			A23 = -M12 / M11;
			break;
		case (APPLY_SCALE):
			M00 = A11;
			M11 = A22;
			if (M00 == 0.0 || M11 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return false;
			}
			A11 = 1.0 / M00;
			// m10 = 0.0;
			// m01 = 0.0;
			A22 = 1.0 / M11;
			// m02 = 0.0;
			// m12 = 0.0;
			break;
		case (APPLY_TRANSLATE):
			// m00 = 1.0;
			// m10 = 0.0;
			// m01 = 0.0;
			// m11 = 1.0;
			A13 = -A13;
			A23 = -A23;
			break;
		case (APPLY_IDENTITY):
			// m00 = 1.0;
			// m10 = 0.0;
			// m01 = 0.0;
			// m11 = 1.0;
			// m02 = 0.0;
			// m12 = 0.0;
			break;
		}
		return true;
	}

	/**
	 * Transforms the specified <code>ptSrc</code> and stores the result in
	 * <code>ptDst</code>. If <code>ptDst</code> is <code>null</code>, a new
	 * {@link Point2D} object is allocated and then the result of the
	 * transformation is stored in this object. In either case,
	 * <code>ptDst</code>, which contains the transformed point, is returned for
	 * convenience. If <code>ptSrc</code> and <code>ptDst</code> are the same
	 * object, the input point is correctly overwritten with the transformed
	 * point.
	 * 
	 * @param ptSrc
	 *            the specified <code>Point2D</code> to be transformed
	 * @param ptDst
	 *            the specified <code>Point2D</code> that stores the result of
	 *            transforming <code>ptSrc</code>
	 * @return the <code>ptDst</code> after transforming <code>ptSrc</code> and
	 *         storing the result in <code>ptDst</code>.
	 * @since 1.2
	 */
	public Point2D transform(Point2D ptSrc, Point2D ptDst) {
		if (ptDst == null) {
			if (ptSrc instanceof Point2D.Double) {
				ptDst = new Point2D.Double();
			} else {
				ptDst = new Point2D.Float();
			}
		}
		// Copy source coords into local variables in case src == dst
		double x = ptSrc.getX();
		double y = ptSrc.getY();
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
			return null;
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			ptDst.setLocation(x * A11 + y * A12 + A13, x * A21 + y * A22 + A23);
			return ptDst;
		case (APPLY_SHEAR | APPLY_SCALE):
			ptDst.setLocation(x * A11 + y * A12, x * A21 + y * A22);
			return ptDst;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
			ptDst.setLocation(y * A12 + A13, x * A21 + A23);
			return ptDst;
		case (APPLY_SHEAR):
			ptDst.setLocation(y * A12, x * A21);
			return ptDst;
		case (APPLY_SCALE | APPLY_TRANSLATE):
			ptDst.setLocation(x * A11 + A13, y * A22 + A23);
			return ptDst;
		case (APPLY_SCALE):
			ptDst.setLocation(x * A11, y * A22);
			return ptDst;
		case (APPLY_TRANSLATE):
			ptDst.setLocation(x + A13, y + A23);
			return ptDst;
		case (APPLY_IDENTITY):
			ptDst.setLocation(x, y);
			return ptDst;
		}

		/* NOTREACHED */
	}

	public void transformPoint2D(double[] srcPts) {

		double x = srcPts[0];
		double y = srcPts[1];
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
			return;
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):

			srcPts[0] = A11 * x + A12 * y + A13;
			srcPts[1] = A21 * x + A22 * y + A23;

			return;
		case (APPLY_SHEAR | APPLY_SCALE):

			srcPts[0] = A11 * x + A12 * y;
			srcPts[1] = A21 * x + A22 * y;
			return;
		case (APPLY_SHEAR | APPLY_TRANSLATE):

			srcPts[0] = A12 * y + A13;
			srcPts[1] = A21 * x + A23;
			return;
		case (APPLY_SHEAR):

			srcPts[0] = A12 * y;
			srcPts[1] = A21 * x;

			return;
		case (APPLY_SCALE | APPLY_TRANSLATE):

			srcPts[0] = A11 * x + A13;
			srcPts[1] = A22 * y + A23;

			return;
		case (APPLY_SCALE):

			srcPts[0] = A11 * x;
			srcPts[1] = A22 * y;

			return;
		case (APPLY_TRANSLATE):

			srcPts[0] = x + A13;
			srcPts[1] = y + A23;

			return;
		case (APPLY_IDENTITY):

			return;
		}

		/* NOTREACHED */
	}

	/**
	 * Transforms an array of point objects by this transform. If any element of
	 * the <code>ptDst</code> array is <code>null</code>, a new
	 * <code>Point2D</code> object is allocated and stored into that element
	 * before storing the results of the transformation.
	 * <p>
	 * Note that this method does not take any precautions to avoid problems
	 * caused by storing results into <code>Point2D</code> objects that will be
	 * used as the source for calculations further down the source array. This
	 * method does guarantee that if a specified <code>Point2D</code> object is
	 * both the source and destination for the same single point transform
	 * operation then the results will not be stored until the calculations are
	 * complete to avoid storing the results on top of the operands. If,
	 * however, the destination <code>Point2D</code> object for one operation is
	 * the same object as the source <code>Point2D</code> object for another
	 * operation further down the source array then the original coordinates in
	 * that point are overwritten before they can be converted.
	 * 
	 * @param ptSrc
	 *            the array containing the source point objects
	 * @param ptDst
	 *            the array into which the transform point objects are returned
	 * @param srcOff
	 *            the offset to the first point object to be transformed in the
	 *            source array
	 * @param dstOff
	 *            the offset to the location of the first transformed point
	 *            object that is stored in the destination array
	 * @param numPts
	 *            the number of point objects to be transformed
	 * @since 1.2
	 */
	public void transform(Point2D[] ptSrc, int srcOff, Point2D[] ptDst,
			int dstOff, int numPts) {
		int state = this.state;
		while (--numPts >= 0) {
			// Copy source coords into local variables in case src == dst
			Point2D src = ptSrc[srcOff++];
			double x = src.getX();
			double y = src.getY();
			Point2D dst = ptDst[dstOff++];
			if (dst == null) {
				if (src instanceof Point2D.Double) {
					dst = new Point2D.Double();
				} else {
					dst = new Point2D.Float();
				}
				ptDst[dstOff - 1] = dst;
			}
			switch (state) {
			default:
				stateError();
				/* NOTREACHED */
				return;
			case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
				dst.setLocation(x * A11 + y * A12 + A13, x * A21 + y * A22
						+ A23);
				break;
			case (APPLY_SHEAR | APPLY_SCALE):
				dst.setLocation(x * A11 + y * A12, x * A21 + y * A22);
				break;
			case (APPLY_SHEAR | APPLY_TRANSLATE):
				dst.setLocation(y * A12 + A13, x * A21 + A23);
				break;
			case (APPLY_SHEAR):
				dst.setLocation(y * A12, x * A21);
				break;
			case (APPLY_SCALE | APPLY_TRANSLATE):
				dst.setLocation(x * A11 + A13, y * A22 + A23);
				break;
			case (APPLY_SCALE):
				dst.setLocation(x * A11, y * A22);
				break;
			case (APPLY_TRANSLATE):
				dst.setLocation(x + A13, y + A23);
				break;
			case (APPLY_IDENTITY):
				dst.setLocation(x, y);
				break;
			}
		}

		/* NOTREACHED */
	}

	/**
	 * Transforms an array of floating point coordinates by this transform. The
	 * two coordinate array sections can be exactly the same or can be
	 * overlapping sections of the same array without affecting the validity of
	 * the results. This method ensures that no source coordinates are
	 * overwritten by a previous operation before they can be transformed. The
	 * coordinates are stored in the arrays starting at the specified offset in
	 * the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
	 * 
	 * @param srcPts
	 *            the array containing the source point coordinates. Each point
	 *            is stored as a pair of x,&nbsp;y coordinates.
	 * @param dstPts
	 *            the array into which the transformed point coordinates are
	 *            returned. Each point is stored as a pair of x,&nbsp;y
	 *            coordinates.
	 * @param srcOff
	 *            the offset to the first point to be transformed in the source
	 *            array
	 * @param dstOff
	 *            the offset to the location of the first transformed point that
	 *            is stored in the destination array
	 * @param numPts
	 *            the number of points to be transformed
	 * @since 1.2
	 */
	public void transform(float[] srcPts, int srcOff, float[] dstPts,
			int dstOff, int numPts) {
		double M00, M01, M02, M10, M11, M12; // For caching
		if (dstPts == srcPts && dstOff > srcOff && dstOff < srcOff + numPts * 2) {
			// If the arrays overlap partially with the destination higher
			// than the source and we transform the coordinates normally
			// we would overwrite some of the later source coordinates
			// with results of previous transformations.
			// To get around this we use arraycopy to copy the points
			// to their final destination with correct overwrite
			// handling and then transform them in place in the new
			// safer location.
			System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
			// srcPts = dstPts; // They are known to be equal.
			srcOff = dstOff;
		}
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
			return;
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			M00 = A11;
			M01 = A12;
			M02 = A13;
			M10 = A21;
			M11 = A22;
			M12 = A23;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				double y = srcPts[srcOff++];
				dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02);
				dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12);
			}
			return;
		case (APPLY_SHEAR | APPLY_SCALE):
			M00 = A11;
			M01 = A12;
			M10 = A21;
			M11 = A22;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				double y = srcPts[srcOff++];
				dstPts[dstOff++] = (float) (M00 * x + M01 * y);
				dstPts[dstOff++] = (float) (M10 * x + M11 * y);
			}
			return;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
			M01 = A12;
			M02 = A13;
			M10 = A21;
			M12 = A23;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02);
				dstPts[dstOff++] = (float) (M10 * x + M12);
			}
			return;
		case (APPLY_SHEAR):
			M01 = A12;
			M10 = A21;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]);
				dstPts[dstOff++] = (float) (M10 * x);
			}
			return;
		case (APPLY_SCALE | APPLY_TRANSLATE):
			M00 = A11;
			M02 = A13;
			M11 = A22;
			M12 = A23;
			while (--numPts >= 0) {
				dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02);
				dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12);
			}
			return;
		case (APPLY_SCALE):
			M00 = A11;
			M11 = A22;
			while (--numPts >= 0) {
				dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]);
				dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]);
			}
			return;
		case (APPLY_TRANSLATE):
			M02 = A13;
			M12 = A23;
			while (--numPts >= 0) {
				dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02);
				dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12);
			}
			return;
		case (APPLY_IDENTITY):
			if (srcPts != dstPts || srcOff != dstOff) {
				System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
			}
			return;
		}

		/* NOTREACHED */
	}

	/**
	 * Transforms an array of double precision coordinates by this transform.
	 * The two coordinate array sections can be exactly the same or can be
	 * overlapping sections of the same array without affecting the validity of
	 * the results. This method ensures that no source coordinates are
	 * overwritten by a previous operation before they can be transformed. The
	 * coordinates are stored in the arrays starting at the indicated offset in
	 * the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
	 * 
	 * @param srcPts
	 *            the array containing the source point coordinates. Each point
	 *            is stored as a pair of x,&nbsp;y coordinates.
	 * @param dstPts
	 *            the array into which the transformed point coordinates are
	 *            returned. Each point is stored as a pair of x,&nbsp;y
	 *            coordinates.
	 * @param srcOff
	 *            the offset to the first point to be transformed in the source
	 *            array
	 * @param dstOff
	 *            the offset to the location of the first transformed point that
	 *            is stored in the destination array
	 * @param numPts
	 *            the number of point objects to be transformed
	 * @since 1.2
	 */
	public void transform(double[] srcPts, int srcOff, double[] dstPts,
			int dstOff, int numPts) {
		double M00, M01, M02, M10, M11, M12; // For caching
		if (dstPts == srcPts && dstOff > srcOff && dstOff < srcOff + numPts * 2) {
			// If the arrays overlap partially with the destination higher
			// than the source and we transform the coordinates normally
			// we would overwrite some of the later source coordinates
			// with results of previous transformations.
			// To get around this we use arraycopy to copy the points
			// to their final destination with correct overwrite
			// handling and then transform them in place in the new
			// safer location.
			System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
			// srcPts = dstPts; // They are known to be equal.
			srcOff = dstOff;
		}
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
			return;
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			M00 = A11;
			M01 = A12;
			M02 = A13;
			M10 = A21;
			M11 = A22;
			M12 = A23;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				double y = srcPts[srcOff++];
				dstPts[dstOff++] = M00 * x + M01 * y + M02;
				dstPts[dstOff++] = M10 * x + M11 * y + M12;
			}
			return;
		case (APPLY_SHEAR | APPLY_SCALE):
			M00 = A11;
			M01 = A12;
			M10 = A21;
			M11 = A22;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				double y = srcPts[srcOff++];
				dstPts[dstOff++] = M00 * x + M01 * y;
				dstPts[dstOff++] = M10 * x + M11 * y;
			}
			return;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
			M01 = A12;
			M02 = A13;
			M10 = A21;
			M12 = A23;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02;
				dstPts[dstOff++] = M10 * x + M12;
			}
			return;
		case (APPLY_SHEAR):
			M01 = A12;
			M10 = A21;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				dstPts[dstOff++] = M01 * srcPts[srcOff++];
				dstPts[dstOff++] = M10 * x;
			}
			return;
		case (APPLY_SCALE | APPLY_TRANSLATE):
			M00 = A11;
			M02 = A13;
			M11 = A22;
			M12 = A23;
			while (--numPts >= 0) {
				dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02;
				dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12;
			}
			return;
		case (APPLY_SCALE):
			M00 = A11;
			M11 = A22;
			while (--numPts >= 0) {
				dstPts[dstOff++] = M00 * srcPts[srcOff++];
				dstPts[dstOff++] = M11 * srcPts[srcOff++];
			}
			return;
		case (APPLY_TRANSLATE):
			M02 = A13;
			M12 = A23;
			while (--numPts >= 0) {
				dstPts[dstOff++] = srcPts[srcOff++] + M02;
				dstPts[dstOff++] = srcPts[srcOff++] + M12;
			}
			return;
		case (APPLY_IDENTITY):
			if (srcPts != dstPts || srcOff != dstOff) {
				System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
			}
			return;
		}

		/* NOTREACHED */
	}

	/**
	 * Transforms an array of floating point coordinates by this transform and
	 * stores the results into an array of doubles. The coordinates are stored
	 * in the arrays starting at the specified offset in the order
	 * <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
	 * 
	 * @param srcPts
	 *            the array containing the source point coordinates. Each point
	 *            is stored as a pair of x,&nbsp;y coordinates.
	 * @param dstPts
	 *            the array into which the transformed point coordinates are
	 *            returned. Each point is stored as a pair of x,&nbsp;y
	 *            coordinates.
	 * @param srcOff
	 *            the offset to the first point to be transformed in the source
	 *            array
	 * @param dstOff
	 *            the offset to the location of the first transformed point that
	 *            is stored in the destination array
	 * @param numPts
	 *            the number of points to be transformed
	 * @since 1.2
	 */
	public void transform(float[] srcPts, int srcOff, double[] dstPts,
			int dstOff, int numPts) {
		double M00, M01, M02, M10, M11, M12; // For caching
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
			return;
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			M00 = A11;
			M01 = A12;
			M02 = A13;
			M10 = A21;
			M11 = A22;
			M12 = A23;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				double y = srcPts[srcOff++];
				dstPts[dstOff++] = M00 * x + M01 * y + M02;
				dstPts[dstOff++] = M10 * x + M11 * y + M12;
			}
			return;
		case (APPLY_SHEAR | APPLY_SCALE):
			M00 = A11;
			M01 = A12;
			M10 = A21;
			M11 = A22;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				double y = srcPts[srcOff++];
				dstPts[dstOff++] = M00 * x + M01 * y;
				dstPts[dstOff++] = M10 * x + M11 * y;
			}
			return;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
			M01 = A12;
			M02 = A13;
			M10 = A21;
			M12 = A23;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02;
				dstPts[dstOff++] = M10 * x + M12;
			}
			return;
		case (APPLY_SHEAR):
			M01 = A12;
			M10 = A21;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				dstPts[dstOff++] = M01 * srcPts[srcOff++];
				dstPts[dstOff++] = M10 * x;
			}
			return;
		case (APPLY_SCALE | APPLY_TRANSLATE):
			M00 = A11;
			M02 = A13;
			M11 = A22;
			M12 = A23;
			while (--numPts >= 0) {
				dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02;
				dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12;
			}
			return;
		case (APPLY_SCALE):
			M00 = A11;
			M11 = A22;
			while (--numPts >= 0) {
				dstPts[dstOff++] = M00 * srcPts[srcOff++];
				dstPts[dstOff++] = M11 * srcPts[srcOff++];
			}
			return;
		case (APPLY_TRANSLATE):
			M02 = A13;
			M12 = A23;
			while (--numPts >= 0) {
				dstPts[dstOff++] = srcPts[srcOff++] + M02;
				dstPts[dstOff++] = srcPts[srcOff++] + M12;
			}
			return;
		case (APPLY_IDENTITY):
			while (--numPts >= 0) {
				dstPts[dstOff++] = srcPts[srcOff++];
				dstPts[dstOff++] = srcPts[srcOff++];
			}
			return;
		}

		/* NOTREACHED */
	}

	/**
	 * Transforms an array of double precision coordinates by this transform and
	 * stores the results into an array of floats. The coordinates are stored in
	 * the arrays starting at the specified offset in the order
	 * <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
	 * 
	 * @param srcPts
	 *            the array containing the source point coordinates. Each point
	 *            is stored as a pair of x,&nbsp;y coordinates.
	 * @param dstPts
	 *            the array into which the transformed point coordinates are
	 *            returned. Each point is stored as a pair of x,&nbsp;y
	 *            coordinates.
	 * @param srcOff
	 *            the offset to the first point to be transformed in the source
	 *            array
	 * @param dstOff
	 *            the offset to the location of the first transformed point that
	 *            is stored in the destination array
	 * @param numPts
	 *            the number of point objects to be transformed
	 * @since 1.2
	 */
	public void transform(double[] srcPts, int srcOff, float[] dstPts,
			int dstOff, int numPts) {
		double M00, M01, M02, M10, M11, M12; // For caching
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
			return;
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			M00 = A11;
			M01 = A12;
			M02 = A13;
			M10 = A21;
			M11 = A22;
			M12 = A23;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				double y = srcPts[srcOff++];
				dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02);
				dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12);
			}
			return;
		case (APPLY_SHEAR | APPLY_SCALE):
			M00 = A11;
			M01 = A12;
			M10 = A21;
			M11 = A22;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				double y = srcPts[srcOff++];
				dstPts[dstOff++] = (float) (M00 * x + M01 * y);
				dstPts[dstOff++] = (float) (M10 * x + M11 * y);
			}
			return;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
			M01 = A12;
			M02 = A13;
			M10 = A21;
			M12 = A23;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02);
				dstPts[dstOff++] = (float) (M10 * x + M12);
			}
			return;
		case (APPLY_SHEAR):
			M01 = A12;
			M10 = A21;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]);
				dstPts[dstOff++] = (float) (M10 * x);
			}
			return;
		case (APPLY_SCALE | APPLY_TRANSLATE):
			M00 = A11;
			M02 = A13;
			M11 = A22;
			M12 = A23;
			while (--numPts >= 0) {
				dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02);
				dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12);
			}
			return;
		case (APPLY_SCALE):
			M00 = A11;
			M11 = A22;
			while (--numPts >= 0) {
				dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]);
				dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]);
			}
			return;
		case (APPLY_TRANSLATE):
			M02 = A13;
			M12 = A23;
			while (--numPts >= 0) {
				dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02);
				dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12);
			}
			return;
		case (APPLY_IDENTITY):
			while (--numPts >= 0) {
				dstPts[dstOff++] = (float) (srcPts[srcOff++]);
				dstPts[dstOff++] = (float) (srcPts[srcOff++]);
			}
			return;
		}

		/* NOTREACHED */
	}

	/**
	 * Inverse transforms the specified <code>ptSrc</code> and stores the result
	 * in <code>ptDst</code>. If <code>ptDst</code> is <code>null</code>, a new
	 * <code>Point2D</code> object is allocated and then the result of the
	 * transform is stored in this object. In either case, <code>ptDst</code>,
	 * which contains the transformed point, is returned for convenience. If
	 * <code>ptSrc</code> and <code>ptDst</code> are the same object, the input
	 * point is correctly overwritten with the transformed point.
	 * 
	 * @param ptSrc
	 *            the point to be inverse transformed
	 * @param ptDst
	 *            the resulting transformed point
	 * @return <code>ptDst</code>, which contains the result of the inverse
	 *         transform.
	 * @exception NoninvertibleTransformException
	 *                if the matrix cannot be inverted.
	 * @since 1.2
	 */
	@SuppressWarnings("fallthrough")
	public Point2D inverseTransform(Point2D ptSrc, Point2D ptDst)
			throws NoninvertibleTransformException {
		if (ptDst == null) {
			if (ptSrc instanceof Point2D.Double) {
				ptDst = new Point2D.Double();
			} else {
				ptDst = new Point2D.Float();
			}
		}
		// Copy source coords into local variables in case src == dst
		double x = ptSrc.getX();
		double y = ptSrc.getY();
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			x -= A13;
			y -= A23;
			/* NOBREAK */
		case (APPLY_SHEAR | APPLY_SCALE):
			double det = A11 * A22 - A12 * A21;
			if (Math.abs(det) <= Double.MIN_VALUE) {
				throw new NoninvertibleTransformException("Determinant is "
						+ det);
			}
			ptDst.setLocation((x * A22 - y * A12) / det, (y * A11 - x * A21)
					/ det);
			return ptDst;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
			x -= A13;
			y -= A23;
			/* NOBREAK */
		case (APPLY_SHEAR):
			if (A12 == 0.0 || A21 == 0.0) {
				throw new NoninvertibleTransformException("Determinant is 0");
			}
			ptDst.setLocation(y / A21, x / A12);
			return ptDst;
		case (APPLY_SCALE | APPLY_TRANSLATE):
			x -= A13;
			y -= A23;
			/* NOBREAK */
		case (APPLY_SCALE):
			if (A11 == 0.0 || A22 == 0.0) {
				throw new NoninvertibleTransformException("Determinant is 0");
			}
			ptDst.setLocation(x / A11, y / A22);
			return ptDst;
		case (APPLY_TRANSLATE):
			ptDst.setLocation(x - A13, y - A23);
			return ptDst;
		case (APPLY_IDENTITY):
			ptDst.setLocation(x, y);
			return ptDst;
		}

		/* NOTREACHED */
	}

	public boolean inverseTransform2D(Point2D ptSrc) {

		// Copy source coords into local variables in case src == dst
		double x = ptSrc.getX();
		double y = ptSrc.getY();
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			x -= A13;
			y -= A23;
			/* NOBREAK */
		case (APPLY_SHEAR | APPLY_SCALE):
			double det = A11 * A22 - A12 * A21;
			if (Math.abs(det) <= Double.MIN_VALUE) {
				// throw new NoninvertibleTransformException("Determinant is "
				// + det);
				return false;
			}
			ptSrc.setLocation((x * A22 - y * A12) / det, (y * A11 - x * A21)
					/ det);
			return true;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
			x -= A13;
			y -= A23;
			/* NOBREAK */
		case (APPLY_SHEAR):
			if (A12 == 0.0 || A21 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return false;
			}
			ptSrc.setLocation(y / A21, x / A12);
			return true;
		case (APPLY_SCALE | APPLY_TRANSLATE):
			x -= A13;
			y -= A23;
			/* NOBREAK */
		case (APPLY_SCALE):
			if (A11 == 0.0 || A22 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return false;
			}
			ptSrc.setLocation(x / A11, y / A22);
			return true;
		case (APPLY_TRANSLATE):
			ptSrc.setLocation(x - A13, y - A23);
			return true;
		case (APPLY_IDENTITY):
			ptSrc.setLocation(x, y);
			return true;
		}

		/* NOTREACHED */

	}

	public boolean inverseTransformPoint(double[] ptDst) {
		double x = ptDst[0];
		double y = ptDst[1];
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			x -= A13;
			y -= A23;
			/* NOBREAK */
		case (APPLY_SHEAR | APPLY_SCALE):
			double det = A11 * A22 - A12 * A21;
			if (Math.abs(det) <= Double.MIN_VALUE) {
				// throw new NoninvertibleTransformException("Determinant is "+
				// det);
				return false;
			}
			ptDst[0] = (x * A22 - y * A12) / det;
			ptDst[1] = (y * A11 - x * A21) / det;
			return true;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
			x -= A13;
			y -= A23;
			/* NOBREAK */
		case (APPLY_SHEAR):
			if (A12 == 0.0 || A21 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return false;
			}
			ptDst[0] = y / A21;
			ptDst[1] = x / A12;
			return true;
		case (APPLY_SCALE | APPLY_TRANSLATE):
			x -= A13;
			y -= A23;
			/* NOBREAK */
		case (APPLY_SCALE):
			if (A11 == 0.0 || A22 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return false;
			}
			ptDst[0] = x / A11;
			ptDst[1] = y / A22;
			return true;
		case (APPLY_TRANSLATE):
			ptDst[0] = x - A13;
			ptDst[1] = y - A23;
			return true;
		case (APPLY_IDENTITY):
			ptDst[0] = x;
			ptDst[1] = y;
			return true;
		}

		/* NOTREACHED */

	}

	/**
	 * Inverse transforms an array of double precision coordinates by this
	 * transform. The two coordinate array sections can be exactly the same or
	 * can be overlapping sections of the same array without affecting the
	 * validity of the results. This method ensures that no source coordinates
	 * are overwritten by a previous operation before they can be transformed.
	 * The coordinates are stored in the arrays starting at the specified offset
	 * in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
	 * 
	 * @param srcPts
	 *            the array containing the source point coordinates. Each point
	 *            is stored as a pair of x,&nbsp;y coordinates.
	 * @param dstPts
	 *            the array into which the transformed point coordinates are
	 *            returned. Each point is stored as a pair of x,&nbsp;y
	 *            coordinates.
	 * @param srcOff
	 *            the offset to the first point to be transformed in the source
	 *            array
	 * @param dstOff
	 *            the offset to the location of the first transformed point that
	 *            is stored in the destination array
	 * @param numPts
	 *            the number of point objects to be transformed
	 * @exception NoninvertibleTransformException
	 *                if the matrix cannot be inverted.
	 * @since 1.2
	 */
	public boolean inverseTransform(double[] srcPts, int srcOff,
			double[] dstPts, int dstOff, int numPts)

	{
		double M00, M01, M02, M10, M11, M12; // For caching
		double det;
		if (dstPts == srcPts && dstOff > srcOff && dstOff < srcOff + numPts * 2) {
			// If the arrays overlap partially with the destination higher
			// than the source and we transform the coordinates normally
			// we would overwrite some of the later source coordinates
			// with results of previous transformations.
			// To get around this we use arraycopy to copy the points
			// to their final destination with correct overwrite
			// handling and then transform them in place in the new
			// safer location.
			System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
			// srcPts = dstPts; // They are known to be equal.
			srcOff = dstOff;
		}
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
			return false;
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
			M00 = A11;
			M01 = A12;
			M02 = A13;
			M10 = A21;
			M11 = A22;
			M12 = A23;
			det = M00 * M11 - M01 * M10;
			if (Math.abs(det) <= Double.MIN_VALUE) {
				// throw new NoninvertibleTransformException("Determinant is "+
				// det);
				return false;
			}
			while (--numPts >= 0) {
				double x = srcPts[srcOff++] - M02;
				double y = srcPts[srcOff++] - M12;
				dstPts[dstOff++] = (x * M11 - y * M01) / det;
				dstPts[dstOff++] = (y * M00 - x * M10) / det;
			}
			return true;
		case (APPLY_SHEAR | APPLY_SCALE):
			M00 = A11;
			M01 = A12;
			M10 = A21;
			M11 = A22;
			det = M00 * M11 - M01 * M10;
			if (Math.abs(det) <= Double.MIN_VALUE) {
				// throw new NoninvertibleTransformException("Determinant is "+
				// det);
				return false;
			}
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				double y = srcPts[srcOff++];
				dstPts[dstOff++] = (x * M11 - y * M01) / det;
				dstPts[dstOff++] = (y * M00 - x * M10) / det;
			}
			return true;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
			M01 = A12;
			M02 = A13;
			M10 = A21;
			M12 = A23;
			if (M01 == 0.0 || M10 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return false;
			}
			while (--numPts >= 0) {
				double x = srcPts[srcOff++] - M02;
				dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M10;
				dstPts[dstOff++] = x / M01;
			}
			return true;
		case (APPLY_SHEAR):
			M01 = A12;
			M10 = A21;
			if (M01 == 0.0 || M10 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return false;
			}
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				dstPts[dstOff++] = srcPts[srcOff++] / M10;
				dstPts[dstOff++] = x / M01;
			}
			return true;
		case (APPLY_SCALE | APPLY_TRANSLATE):
			M00 = A11;
			M02 = A13;
			M11 = A22;
			M12 = A23;
			if (M00 == 0.0 || M11 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return false;
			}
			while (--numPts >= 0) {
				dstPts[dstOff++] = (srcPts[srcOff++] - M02) / M00;
				dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M11;
			}
			return true;
		case (APPLY_SCALE):
			M00 = A11;
			M11 = A22;
			if (M00 == 0.0 || M11 == 0.0) {
				// throw new
				// NoninvertibleTransformException("Determinant is 0");
				return false;
			}
			while (--numPts >= 0) {
				dstPts[dstOff++] = srcPts[srcOff++] / M00;
				dstPts[dstOff++] = srcPts[srcOff++] / M11;
			}
			return true;
		case (APPLY_TRANSLATE):
			M02 = A13;
			M12 = A23;
			while (--numPts >= 0) {
				dstPts[dstOff++] = srcPts[srcOff++] - M02;
				dstPts[dstOff++] = srcPts[srcOff++] - M12;
			}
			return true;
		case (APPLY_IDENTITY):
			if (srcPts != dstPts || srcOff != dstOff) {
				System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
			}
			return true;
		}

		/* NOTREACHED */
	}

	/**
	 * Transforms the relative distance vector specified by <code>ptSrc</code>
	 * and stores the result in <code>ptDst</code>. A relative distance vector
	 * is transformed without applying the translation components of the affine
	 * transformation matrix using the following equations:
	 * 
	 * <pre>
	 *  [  x' ]   [  m00  m01 (m02) ] [  x  ]   [ m00x + m01y ]
	 *  [  y' ] = [  m10  m11 (m12) ] [  y  ] = [ m10x + m11y ]
	 *  [ (1) ]   [  (0)  (0) ( 1 ) ] [ (1) ]   [     (1)     ]
	 * </pre>
	 * 
	 * If <code>ptDst</code> is <code>null</code>, a new <code>Point2D</code>
	 * object is allocated and then the result of the transform is stored in
	 * this object. In either case, <code>ptDst</code>, which contains the
	 * transformed point, is returned for convenience. If <code>ptSrc</code> and
	 * <code>ptDst</code> are the same object, the input point is correctly
	 * overwritten with the transformed point.
	 * 
	 * @param ptSrc
	 *            the distance vector to be delta transformed
	 * @param ptDst
	 *            the resulting transformed distance vector
	 * @return <code>ptDst</code>, which contains the result of the
	 *         transformation.
	 * @since 1.2
	 */
	public Point2D deltaTransform(Point2D ptSrc, Point2D ptDst) {
		if (ptDst == null) {
			if (ptSrc instanceof Point2D.Double) {
				ptDst = new Point2D.Double();
			} else {
				ptDst = new Point2D.Float();
			}
		}
		// Copy source coords into local variables in case src == dst
		double x = ptSrc.getX();
		double y = ptSrc.getY();
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
			return null;
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
		case (APPLY_SHEAR | APPLY_SCALE):
			ptDst.setLocation(x * A11 + y * A12, x * A21 + y * A22);
			return ptDst;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
		case (APPLY_SHEAR):
			ptDst.setLocation(y * A12, x * A21);
			return ptDst;
		case (APPLY_SCALE | APPLY_TRANSLATE):
		case (APPLY_SCALE):
			ptDst.setLocation(x * A11, y * A22);
			return ptDst;
		case (APPLY_TRANSLATE):
		case (APPLY_IDENTITY):
			ptDst.setLocation(x, y);
			return ptDst;
		}

		/* NOTREACHED */
	}

	/**
	 * Transforms an array of relative distance vectors by this transform. A
	 * relative distance vector is transformed without applying the translation
	 * components of the affine transformation matrix using the following
	 * equations:
	 * 
	 * <pre>
	 *  [  x' ]   [  m00  m01 (m02) ] [  x  ]   [ m00x + m01y ]
	 *  [  y' ] = [  m10  m11 (m12) ] [  y  ] = [ m10x + m11y ]
	 *  [ (1) ]   [  (0)  (0) ( 1 ) ] [ (1) ]   [     (1)     ]
	 * </pre>
	 * 
	 * The two coordinate array sections can be exactly the same or can be
	 * overlapping sections of the same array without affecting the validity of
	 * the results. This method ensures that no source coordinates are
	 * overwritten by a previous operation before they can be transformed. The
	 * coordinates are stored in the arrays starting at the indicated offset in
	 * the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
	 * 
	 * @param srcPts
	 *            the array containing the source distance vectors. Each vector
	 *            is stored as a pair of relative x,&nbsp;y coordinates.
	 * @param dstPts
	 *            the array into which the transformed distance vectors are
	 *            returned. Each vector is stored as a pair of relative
	 *            x,&nbsp;y coordinates.
	 * @param srcOff
	 *            the offset to the first vector to be transformed in the source
	 *            array
	 * @param dstOff
	 *            the offset to the location of the first transformed vector
	 *            that is stored in the destination array
	 * @param numPts
	 *            the number of vector coordinate pairs to be transformed
	 * @since 1.2
	 */
	public void deltaTransform(double[] srcPts, int srcOff, double[] dstPts,
			int dstOff, int numPts) {
		double M00, M01, M10, M11; // For caching
		if (dstPts == srcPts && dstOff > srcOff && dstOff < srcOff + numPts * 2) {
			// If the arrays overlap partially with the destination higher
			// than the source and we transform the coordinates normally
			// we would overwrite some of the later source coordinates
			// with results of previous transformations.
			// To get around this we use arraycopy to copy the points
			// to their final destination with correct overwrite
			// handling and then transform them in place in the new
			// safer location.
			System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
			// srcPts = dstPts; // They are known to be equal.
			srcOff = dstOff;
		}
		switch (state) {
		default:
			stateError();
			/* NOTREACHED */
			return;
		case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
		case (APPLY_SHEAR | APPLY_SCALE):
			M00 = A11;
			M01 = A12;
			M10 = A21;
			M11 = A22;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				double y = srcPts[srcOff++];
				dstPts[dstOff++] = x * M00 + y * M01;
				dstPts[dstOff++] = x * M10 + y * M11;
			}
			return;
		case (APPLY_SHEAR | APPLY_TRANSLATE):
		case (APPLY_SHEAR):
			M01 = A12;
			M10 = A21;
			while (--numPts >= 0) {
				double x = srcPts[srcOff++];
				dstPts[dstOff++] = srcPts[srcOff++] * M01;
				dstPts[dstOff++] = x * M10;
			}
			return;
		case (APPLY_SCALE | APPLY_TRANSLATE):
		case (APPLY_SCALE):
			M00 = A11;
			M11 = A22;
			while (--numPts >= 0) {
				dstPts[dstOff++] = srcPts[srcOff++] * M00;
				dstPts[dstOff++] = srcPts[srcOff++] * M11;
			}
			return;
		case (APPLY_TRANSLATE):
		case (APPLY_IDENTITY):
			if (srcPts != dstPts || srcOff != dstOff) {
				System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
			}
			return;
		}

		/* NOTREACHED */
	}

	/**
	 * Returns a new {@link Shape} object defined by the geometry of the
	 * specified <code>Shape</code> after it has been transformed by this
	 * transform.
	 * 
	 * @param pSrc
	 *            the specified <code>Shape</code> object to be transformed by
	 *            this transform.
	 * @return a new <code>Shape</code> object that defines the geometry of the
	 *         transformed <code>Shape</code>, or null if {@code pSrc} is null.
	 * @since 1.2
	 */


	// Round values to sane precision for printing
	// Note that Math.sin(Math.PI) has an error of about 10^-16
	private static double _matround(double matval) {
		return Math.rint(matval * 1E15) / 1E15;
	}

	/**
	 * Returns a <code>String</code> that represents the value of this
	 * {@link Object}.
	 * 
	 * @return a <code>String</code> representing the value of this
	 *         <code>Object</code>.
	 * @since 1.2
	 */
	public String toString() {
		return ("AffineTransform[[" + _matround(A11) + ", " + _matround(A12)
				+ ", " + _matround(A13) + "], [" + _matround(A21) + ", "
				+ _matround(A22) + ", " + _matround(A23) + "]]");
	}

	/**
	 * Returns <code>true</code> if this <code>AffineTransform</code> is an
	 * identity transform.
	 * 
	 * @return <code>true</code> if this <code>AffineTransform</code> is an
	 *         identity transform; <code>false</code> otherwise.
	 * @since 1.2
	 */
	public boolean isIdentity() {
		return (state == APPLY_IDENTITY || (getType() == TYPE_IDENTITY));
	}

	/**
	 * Returns a copy of this <code>AffineTransform</code> object.
	 * 
	 * @return an <code>Object</code> that is a copy of this
	 *         <code>AffineTransform</code> object.
	 * @since 1.2
	 */
	public Object clone() {
		try {
			return super.clone();
		} catch (CloneNotSupportedException e) {
			// this shouldn't happen, since we are Cloneable
			return null;
		}
	}

	/**
	 * Returns the hashcode for this transform.
	 * 
	 * @return a hash code for this transform.
	 * @since 1.2
	 */
	public int hashCode() {
		long bits = Double.doubleToLongBits(A11);
		bits = bits * 31 + Double.doubleToLongBits(A12);
		bits = bits * 31 + Double.doubleToLongBits(A13);
		bits = bits * 31 + Double.doubleToLongBits(A21);
		bits = bits * 31 + Double.doubleToLongBits(A22);
		bits = bits * 31 + Double.doubleToLongBits(A23);
		return (((int) bits) ^ ((int) (bits >> 32)));
	}

	/**
	 * Returns <code>true</code> if this <code>AffineTransform</code> represents
	 * the same affine coordinate transform as the specified argument.
	 * 
	 * @param obj
	 *            the <code>Object</code> to test for equality with this
	 *            <code>AffineTransform</code>
	 * @return <code>true</code> if <code>obj</code> equals this
	 *         <code>AffineTransform</code> object; <code>false</code>
	 *         otherwise.
	 * @since 1.2
	 */
	public boolean equals(Object obj) {
		if (!(obj instanceof CopyOfAffineTransform)) {
			return false;
		}

		CopyOfAffineTransform a = (CopyOfAffineTransform) obj;

		return ((A11 == a.A11) && (A12 == a.A12) && (A13 == a.A13)
				&& (A21 == a.A21) && (A22 == a.A22) && (A23 == a.A23));
	}

	/*
	 * Serialization support. A readObject method is neccessary because the
	 * state field is part of the implementation of this particular
	 * AffineTransform and not part of the public specification. The state
	 * variable's value needs to be recalculated on the fly by the readObject
	 * method as it is in the 6-argument matrix constructor.
	 */

	/*
	 * JDK 1.2 serialVersionUID
	 */
	private static final long serialVersionUID = 1330973210523860834L;

	private void writeObject(java.io.ObjectOutputStream s)
			throws java.lang.ClassNotFoundException, java.io.IOException {
		s.defaultWriteObject();
	}

	private void readObject(java.io.ObjectInputStream s)
			throws java.lang.ClassNotFoundException, java.io.IOException {
		s.defaultReadObject();
		updateState();
	}

	public void transformRectangle2D(double[] bounds) {
		int i = 0;
		double x, y;
		double xmin = Double.POSITIVE_INFINITY;
		double ymin = Double.POSITIVE_INFINITY;
		double xmax = Double.NEGATIVE_INFINITY;
		double ymax = Double.NEGATIVE_INFINITY;

		this.transform(bounds, 0, bounds, 0, 4);
		for (; i < 4; ++i) {
			x = bounds[2 * i];
			y = bounds[1 + 2 * i];
			if (x < xmin)
				xmin = x;
			if (x > xmax)
				xmax = x;
			if (y < ymin)
				ymin = y;
			if (y > ymax)
				ymax = y;
		}
		bounds[0] = xmin;
		bounds[1] = xmax;
		bounds[2] = ymin;
		bounds[3] = ymax;

	}

	public double affine_matrix_get_scaleX0() {
		if (0.0 == this.A11)
			return Math.abs(this.A12);
		if (0.0 == this.A12)
			return Math.abs(this.A11);
		return Math.sqrt(this.A11 * (this.A11) + this.A12 * (this.A12));

	}

	public double affine_matrix_get_scaleY0() {
		if (0.0 == this.A22)
			return Math.abs(this.A21);
		if (0.0 == this.A21)
			return Math.abs(this.A22);
		return Math.sqrt(this.A21 * (this.A21) + this.A22 * (this.A22));
	}

	int getFlip() {
		int scaleX = 0, scaleY = 0, shearX = 0, shearY = 0;
		scaleX = (0.0 == this.A11) ? 0 : ((this.A11 > 0.0) ? 1 : -1);
		scaleY = (0.0 == this.A22) ? 0 : ((this.A22 > 0.0) ? 1 : -1);
		shearX = (0.0 == this.A12) ? 0 : ((this.A12 > 0.0) ? 1 : -1);
		shearY = (0.0 == this.A21) ? 0 : ((this.A21 > 0.0) ? 1 : -1);
		if (scaleX == scaleY && shearX == -shearY)
			return 1;
		if (scaleX == -scaleY && shearX == shearY)
			return -1;
		return 0;
	}

	public double getRotation() {
		int flip = getFlip();
		if (flip != 0) {
			double scaleX;
			double scaleY;
			double x, y;
			scaleX = affine_matrix_get_scaleX0();
			scaleY = affine_matrix_get_scaleY0() * flip;
			y = this.A21 / scaleY - this.A12 / scaleX;
			x = this.A22 / scaleY + this.A11 / scaleX;
			if (y > -1e-64 && y < 1e-64)
				y = 0.0d;
			if (x > -1e-64 && x < 1e-64)
				x = 0.0d;
			return Math.atan2(y, x);
		}
		// return Double.NaN;
		return 0;
	}

	public static void multiply(CopyOfAffineTransform result,
			final CopyOfAffineTransform a, final CopyOfAffineTransform b) {
		double m00, m10, m01, m11, m02, m12;
		m00 = a.A11 * b.A11 + a.A21 * b.A12;
		m10 = a.A11 * b.A21 + a.A21 * b.A22;

		m01 = a.A12 * b.A11 + a.A22 * b.A12;
		m11 = a.A12 * b.A21 + a.A22 * b.A22;

		m02 = a.A13 * b.A11 + a.A23 * b.A12 + b.A13;
		m12 = a.A13 * b.A21 + a.A23 * b.A22 + b.A23;

		if (1e-100 > m00 && -1e-100 < m00)
			m00 = 0;
		if (1e-100 > m10 && -1e-100 < m10)
			m10 = 0;
		if (1e-100 > m01 && -1e-100 < m01)
			m01 = 0;
		if (1e-100 > m11 && -1e-100 < m11)
			m11 = 0;

		result.A11 = m00;
		result.A21 = m10;
		result.A12 = m01;
		result.A22 = m11;
		result.A13 = m02;
		result.A23 = m12;
		result.updateState();
	}

	public double getScale() {
		double scale = A11;
		double shear = A12;
		if (shear == 0)
			return Math.abs(scale); // Optimization for a very common case.
		if (scale == 0)
			return Math.abs(shear); // Not as common as above, but still common
									// enough.
		return Math.sqrt(scale * scale + shear * shear);
	}

	public String onSavState() {
		byte[] bs = new byte[48];
		java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(bs);
		buf.order(ByteOrder.LITTLE_ENDIAN);
		buf.putDouble(A11);
		buf.putDouble(A21);
		buf.putDouble(A12);
		buf.putDouble(A22);
		buf.putDouble(A13);
		buf.putDouble(A23);
		return WKBWriter.toHex(bs);
	}

	public void onRestoreState(String hex) {
		double m00, m10, m01, m11, m02, m12;
		byte[] bs = WKBReader.hexToBytes(hex);
		java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(bs);
		buf.order(ByteOrder.LITTLE_ENDIAN);
		m00 = buf.getDouble();
		m10 = buf.getDouble();
		m01 = buf.getDouble();
		m11 = buf.getDouble();
		m02 = buf.getDouble();
		m12 = buf.getDouble();
		this.A11 = m00;
		this.A21 = m10;
		this.A12 = m01;
		this.A22 = m11;
		this.A13 = m02;
		this.A23 = m12;
		this.updateState();
	}
}
